在某个比赛调试程序时遇到的未添加static导致的编译优化问题,起因是和队友分别负责了具有相似结构仅针对数据规模特化的两份代码的编写。但性能差别较大。测试环境为:

  • kunpeng单NUMA node
  • bisheng编译器

经排查发现有两个原因导致了性能的差距:

  • 全局变量作用域为源代码文件但未加static
  • 静态全局变量是否为常值

第一个问题是调试时发现对于作用域为源代码文件的全局变量,添加static的性能明显提升,应该是限定无外部链接性后编译器做了一些优化。

第二个问题是一个循环中使用了静态全局变量作为循环边界条件,包含该循环的函数会被频繁调用,两个静态全局变量值是相同的,都可以作为循环变量边界,但性能差别很大:

1
2
3
4
5
6
7
8
9
static int nr = 8
static int snr = 8;
void func(...){
for(int i=0;i<m;i++){
for(int j=0;j<nr;j++){ //snr性能更好
//process
}
}
}

使用snr的相较于nr有3x的加速比,原因是nr在外层函数调用中曾有过一次赋值,而snr仅有文件顶部这一次赋值,所以导致了这个位置编译器所作的展开等优化策略不同。实际上这里的nr和snr都是和机器向量寄存器长度相关的,考虑到最终比赛的机器向量寄存器位数未知,我在最外层函数入口获取向量寄存器长度,再赋值nr,队友直接默认为256bit,这个差别导致编译器对该循环做的优化不同。该问题还与具体计算有关,因为这个函数是核心kernel,反复调用,而计算部分又因为没有比赛机器的指令集使用了标量模拟计算指令,使编译器能做的优化空间很大,才产生了这个问题。