【发布时间】:2011-12-31 19:31:53
【问题描述】:
我在这里的第一篇文章。一个很棒的网站和资源。
我确实搜索了一下,并查看了标题相似的问题,但找不到关于此的具体内容。
我正在尝试从我的 C++ 程序使用的 C 天文计算库中删除任何冗余和膨胀。我运行了一个简单的分析器 (VerySleepy)。
这是分析器显示为使用时间最多的代码(除了 C 库函数 sprintf 等):
double swi_echeb(const double x, const double* const coef, const int ncf)
{
int j = ncf - 1;
double x2, br, brp2, brpp;
x2 = x * 2.;
br = 0.;
brp2 = 0.; /* dummy assign to silence gcc warning */
brpp = 0.;
for (; j >= 0; --j) { // <-- 0.39s
brp2 = brpp; // <-- 0.01s
brpp = br; // <-- 0.32s
br = x2 * brpp - brp2 + coef[j]; // <-- 3.49s ***
} // <-- 0.14s
return (br - brp2) * .5; // <-- 0.06s
} // <-- 0.05s
这个特定的函数深深嵌入到其他函数中,我的程序调用的主要“启动”函数被调用了数千次。
您可以看到 3.49 秒的出色语句远高于所有其他语句时间。我知道有一些方法可以在可能的情况下使用乘除除法来加速 C 算术。但我知道的不多。
喜欢:
-
将这个语句分成更小的部分会更好吗?:
br = x2 * brpp;br -= brp2;br += coef[j];
任何其他想法或批评。我没有编写这段代码,虽然我确实将 const 添加到函数参数中,因为我喜欢 const 正确性。
我以前从未尝试过使用寄存器或其他花哨的技巧来加快速度。有人认为这样的东西可以在这里工作吗?
我知道人们会说,“试试看!”因此,如果它对有类似算术问题的任何人有所帮助,我会并且会更新我得到的信息。
编辑:发布我根据建议测试过的结果
按照从最快到最慢的顺序,这是我目前发现的。 Profiler 非常困倦。编译器是 Visual Studio 2008 Pro Ed。库和我的应用程序的编译选项是:
Debug, C7 format, /O2 /Ob2 /Oi /Ot /Oy /GT /GL /GF /FD /MTd /GS- /Gy /fp:fast /FAs
以下是 Andrew 关于执行“每个循环 4 次迭代”的建议。这是迄今为止最快的。
在函数中花费的总时间(函数中其他语句的时间未在此处显示)= 2.08 秒
for (; index >= 3; index -= 4) { // 0.02s
brp2 = brpp;
brpp = br; // 0.02s
br = x2 * brpp - brp2 + coef[index]; // 0.25s
brp2 = brpp;
brpp = br; // 0.13s
br = x2 * brpp - brp2 + coef[index - 1]; // 0.33s
brp2 = brpp;
brpp = br; // 0.13s
br = x2 * brpp - brp2 + coef[index - 2]; // 0.34s
brp2 = brpp;
brpp = br; // 0.14s
br = x2 * brpp - brp2 + coef[index - 3]; // 0.42s
}
for (; index >= 0; --index) { // 0.03s
brp2 = brpp; // 0.03s
brpp = br;
br = x2 * brpp - brp2 + coef[index]; // 0.11s
}
第二快的是原始未更改的代码,在函数内总时间为 2.39 秒,同样包括循环外的语句。请注意,这比我原来的帖子少。我最初的帖子是未优化的代码,但由于大家都建议这样做,我的所有测试随后都在 VS08 中尽可能优化:
for (j = ncf - 1; j >= 0; j--) { // 0.02s
brp2 = brpp; // 0.03s
brpp = br; // 0.07s
br = x2 * brpp - brp2 + coef[j]; // 2.14s
}
在这段原始代码之后,下一个最快的是 Drew 的想法,即提前设置指针并使用它。 函数内花费的总时间为 2.49 秒,包括循环外语句的时间:
for (; index >= coef; --index) { // 0.01s
brp2 = brpp;
brpp = br; // 0.06s
br = x2 * brpp - brp2 + *index; // 2.24s
}
我还尝试混合使用 Andrew 的循环展开和 Drew 的指针使用,但这需要 2.39 秒,与未更改的代码相同。
根据结果,循环展开是我目前使用的方法。
【问题讨论】:
-
...您在 O3 尝试过优化器?
-
原型应该是
swi_echeb(double x, const double *coef, int ncf)。额外的const限定符不会提高代码的 const 正确性,它们只会使阅读代码的人感到震惊和烦恼。 -
到目前为止,这里有很多好的建议。有展开循环、前向递增索引、使用
-O3优化标志等。你能独立测试每一个的性能并报告每个有多大的影响吗?这种信息对后代非常有用。 -
@user11234607,请查看此文档:edp.org/work/Construction.pdf 它特定于 Altivec 处理器上的 FFT 实现,但有一些非常有趣的性能调整技巧,适用于编译器和 CPU架构。
-
@DietrichEpp:同意它们应该在原型中(前向声明)。但是我们只看到定义,并且在局部变量(包括参数)上使用
const是一种很好的做法。
标签: c performance math optimization loops