编辑我删除了问题的一部分,因为它可能会令人困惑,感谢答案,我应该能够自己发现。
我试图写一个程序,使用频谱函数对信号进行重采样。我用C写代码,但我发现用C++编码的问题。
下面是我使用的部分代码。我有一个函数test()
,它调用另一个函数test3(tmp)
,传递一个数组作为指针。只要我合并组合+=
运算符,并试图填充第一个函数的数组,我的代码就会变慢。
我初始化和计算使用零,但所有数组必须有双精度值。
#include <stdio.h>
#include <stdlib.h>
void test3(double *tmp);
void test() {
double *tmp;
int k;
tmp = (double *) calloc(250 * 6, sizeof(double));
for (k = 0; k < 1000; ++k)
test3(tmp);
free(tmp);
}
void test3(double *tmp) {
int k;
double *fx;
double *srf;
double *ptr;
int p;
int l;
int c;
double a;
fx = (double *) calloc(2100 * 6, sizeof(double));
srf = (double *) calloc(2100 * 250, sizeof(double));
for (int k = 0; k < 2100 * 6; ++k) fx[k] = 0;
for (int k = 0; k < 2100 * 250; ++k) srf[k] = 0;
ptr = (double *) calloc(250 * 6, sizeof(double));
for (p = 0; p < 6; ++p) {
for (l = 0; l < 250; ++l) {
ptr[l + p*250] = 0.0;
for (c = 0; c < 2100; ++c)
ptr[l+p*250] += fx[c+p*2100] * srf[c+l*2100];
}
}
for (k = 0; k < 250 * 6; ++k) tmp[k] = ptr[k];
free(ptr);
free(fx);
free(srf);
}
int main(int argc, char *argv[]) {
test();
return 0;
}
字符串
我编译使用:
gcc -O2 -o test test.c -lm
型
当我运行代码time ./test
时,
./test_lut 3.84s user 0.11s system 98% cpu 4.008 total
型
2条答案
按热度按时间o4hqfura1#
编译器在优化无用代码方面做得很好,但并不总是这样:
for (k = 0; k < 250*6; ++k) tp[k] = ptr[k];
这行,你基本上就删除了函数test3()
的所有副作用:初始化fx
和srf
指向的数组,计算ptr
指向的数组中的条目,然后释放这些数组。编译器似乎能够确定没有对象受到影响,从函数返回后存活,并完全删除代码,导致几乎没有时间花费在函数上。+=
更改为=
,字符串
保持修改相同的位置,因此只有最后一次迭代是有用的,因此代码减少到
ptr[l+p*250] += fx[2099+p*2100] * srf[2099+l*2100];
,从而导致更快的操作。a
的第三次尝试没有影响,因为编译器可能已经优化了循环外的常量目的地。您可以使用Godbolt's Compiler Explorer研究编译器的工作,并使用代码和编译器标志。
分析生成的代码可以看出,gcc和clang都不会为初始化循环生成任何代码(将
calloc()
分配的内存设置为所有位零值0.0
没有效果)并将最后的for
循环转换为对memcpy
的调用。注解这个最后的for
循环会导致两个编译器都不生成代码,将+=
更改为=
简化了内部循环代码。有趣的是,尽管两个编译器都正确地确定了
fx
和srf
指向所有元素都等于0.0
的数组,但它们并没有假设这些数组的内容在整个计算过程中保持为空,因此目标数组中的所有结果也应该为空,因为ptr[l+p*250] += fx[c+p*2100] * srf[c+l*2100];
简化为ptr[l+p*250] += 0.0;
如果将代码编译为C(而不是C++),并将
fx
、srf
和ptr
定义为型
两个编译器都应该确定数组
fx
和srf
不能被修改,因为通过ptr
写入的副作用,因此保持为null。生成的代码将大大简化,几乎可以立即执行,但这种优化似乎并不有效。zbsbpyhn2#
当
for (k = 0; k < 250*6; ++k) tmp[k] = ptr[k];
被删除时,test3
没有副作用(不会为程序产生任何结果),而the compiler optimizesmain
to the two instructionsxor eax, eax
andret
。当
+=
改为=
时,ptr[l+p*250] = fx[c+p*2100] * srf[c+l*2100];
在ptr[l+p*250]
中留下的值只是for (c = 0; c < 2100; ++c)
的最后一次迭代的值,因此编译器能够用c
为2099的单次迭代替换循环。