代码看起来像这样,内部循环需要大量时间:
#define _table_derive ((double*)(Buffer_temp + offset))
#define Table_derive(m,nbol,pos) _table_derive[(m) + 5*((pos) + _interval_derive_dIdQ * (nbol))]
char *Buffer_temp=malloc(...);
for (n_bol=0; n_bol<1400; n_bol++) // long loop here
[lots of code here, hundreds of lines with computations on doubles, other loops, etc]
double ddI=0, ddQ=0;
// This is the original code
for(k=0; k< 100; k++ ) {
ddI += Table_derive(2,n_bol,k);
ddQ += Table_derive(3,n_bol,k);
}
ddI /= _interval_derive_dIdQ;
ddQ /= _interval_derive_dIdQ;
[more code here]
}
oprofile 告诉我大部分运行时间都花在这里(第二列是时间百分比):
129304 7.6913 :for(k=0; k< 100; k++) {
275831 16.4070 :ddI += Table_derive(2,n_bol,k);
764965 45.5018 :ddQ += Table_derive(3,n_bol,k);
我的第一个问题是:我是否可以依靠 oprofile 来指示代码运行缓慢的正确位置(我在 -Og 和 -Ofast 中尝试过,它基本相同)。
我的第二个问题是:为什么这个非常简单的循环比 sqrt、atan2 和之前的数百行计算慢?我知道我没有显示所有代码,但是有很多代码,对我来说没有意义。
我尝试了各种优化器技巧来矢量化(不起作用)或展开(起作用)但收效甚微,例如:
typedef double aligned_double __attribute__((aligned(8)));
typedef const aligned_double* SSE_PTR;
SSE_PTR TD=(SSE_PTR)&Table_derive(2,n_bol,0); // We KNOW the alignement is correct because offset is multiple of 8
for(k=0; k< 100; k++, TD+=5) {
#pragma Loop_Optimize Unroll No_Vector
ddI += TD[0];
ddQ += TD[1];
}
我检查了优化器的输出:“-Ofast -g -march=native -fopt-info-all=missed.info -funroll-loops”,在这种情况下,我得到“循环展开 9 次”,但如果我尝试矢量化,我得到(简而言之):“无法强制对齐参考”,“矢量对齐可能无法到达”,“矢量化未对齐的访问”,“访问的未知对齐:*(prephitmp_3784 +((sizetype ) _1328 + (long unsigned int) (n_bol_1173 * 500) * 2) * 4)"
有什么办法可以加快速度吗?
附录:谢谢大家的意见,我会在这里回答:
- 是的,我知道代码很丑(不是我的),而且你还没有看到真正的原始代码(这是一个巨大的简化)
- 我坚持使用这个数组,因为 C 代码位于库中,而大型数组一旦被 C 处理和修改,就会传递给调用者(IDL、Python 或 C)。
- 我知道使用一些结构而不是将 char* 转换为复杂的多维 double* 会更好,但请参见上文。当第一次编写这个程序时,结构可能不是 C 规范的一部分(开个玩笑......也许)
- 我知道对于矢量化器来说,数组结构比结构数组更好,但是,叹息......见上文。
- 有一个实际的外循环(在调用程序中),所以这个整体数组的总大小约为 2Gb
- 照原样,在没有优化的情况下运行大约需要 15 分钟,并且在我重写了一些代码(更快的 atan2,数组内的一些手动对齐......)之后一分钟,我使用了 -Ofast 和 -march=native
- 由于硬件的限制变化,我试图更快地跟上数据流。
- 我尝试使用 Clang 并且收益很小(几秒钟),但我没有看到获得优化报告的选项,例如 -fopt-info。我是否必须将程序集视为了解发生了什么的唯一选择?
- 该系统是一个拥有 500Gb RAM 的 64 核,但我无法插入任何 OpenMP pragma 来并行化上述代码(我已经尝试过):它读取一个文件,将其完全解压缩到内存中(2Gb) ,按顺序分析它(诸如'+='之类的东西)并将一些结果吐出给调用IDL/Python。全部在一个内核上(但其他内核非常忙于实际采集和后期处理)。:(
- 没用,感谢您的出色建议:删除 ddQ += ... 似乎将时间百分比转移到上一行:376280 39.4835:ddI+=...
- 这让我们变得更好:删除两者(因此整个循环)保存......什么都没有!所以我想正如彼得所说,我不能相信探查器。如果我分析无环编,我会得到更均匀的时间分布(以前只有 1 秒以上的 3 行,现在大约 10 行,所有这些都像简单的变量分配一样荒谬)。
我猜这个内部循环从一开始就是一个红鲱鱼。我将使用手动计时重新开始优化。谢谢。