为什么代码的位置会影响C ++的性能?

时间:2019-05-25 20:13:11

标签: c++ performance

我正在运行测试性能,发现更改代码顺序可以使其更快而又不影响结果。

性能是通过使用 chrono 库执行时间来衡量的。

vector< vector<float> > U(matrix_size, vector<float>(matrix_size,14));
vector< vector<float> > L(matrix_size, vector<float>(matrix_size,12));
vector< vector<float> > matrix_positive_definite(matrix_size, vector<float>(matrix_size,23));

for (i = 0; i < matrix_size; ++i) {         
   for(j= 0; j < matrix_size; ++j){
//Part II : ________________________________________
    float sum2=0;               
    for(k= 0; k <= (i-1); ++k){
      float sum2_temp=L[i][k]*U[k][j];
      sum2+=sum2_temp;
    }
//Part I : _____________________________________________
    float sum1=0;       
    for(k= 0; k <= (j-1); ++k){
      float sum1_temp=L[i][k]*U[k][j];
      sum1+=sum1_temp;
    }           
//__________________________________________
    if(i>j){
      L[i][j]=(matrix_positive_definite[i][j]-sum1)/U[j][j]; 
    }
    else{
       U[i][j]=matrix_positive_definite[i][j]-sum2;
    }   
   }
}

我使用g++ -O3进行编译(Intel i5 / Win10中为GCC 7.4.0)。 我更改了第一部分和第二部分的顺序,如果第二部分在第一部分之前执行,则会得到更快的结果。

这是整个程序的link

3 个答案:

答案 0 :(得分:5)

我会尝试使用perf stat -d <app>运行两个版本,看看性能计数器的区别在哪里。

基准测试时,您可能希望固定CPU频率,因此它不会影响您的成绩。


在32字节边界上对齐循环通常可以将性能提高8-30%。有关更多详细信息,请参见Causes of Performance Instability due to Code Placement in X86 - Zia Ansari, Intel

尝试使用-O3 -falign-loops=32 -falign-functions=32 -march=native -mtune=native编译代码。

答案 1 :(得分:2)

在运行提供的程序时运行perf stat -ddd表明,这两个版本之间的主要区别主要在于预取。

part II -> part I   and   part I -> part II (original program)
   73,069,502      L1-dcache-prefetch-misses

part II -> part I   and   part II -> part I (only the efficient version)
   31,719,117      L1-dcache-prefetch-misses

part I -> part II   and   part I -> part II (only the less efficient version)
  114,520,949      L1-dcache-prefetch-misses

nb:根据编译器浏览器,part II -> part Ipart I -> part II非常相似。

我猜想,在i上的第一次迭代中,part II几乎没有任何作用,但是在j上的迭代使part I访问U[k][j]的原因是模式可以简化针对i的下一次迭代的预取。

答案 2 :(得分:1)

更快的版本类似于在if (i > j)中移动循环时获得的性能。

if (i > j) {
    float sum1 = 0;
    for (std::size_t k = 0; k < j; ++k){
        sum1 += L_series[i][k] * U_series[k][j];
    }
    L_parallel[i][j] = matrix_positive_definite[i][j] - sum1;
        L[i][j] /= U[j][j];
}
if (i <= j) {
    float sum2 = 0;
    for (std::size_t k = 0; k < i; ++k){
        sum2 += L_series[i][k] * U_series[k][j];
    }
    U_parallel[i][j] = matrix_positive_definite[i][j] - sum2;
}

因此,我假设在一种情况下,编译器能够自行进行转换。对我而言,它仅发生在-O3。 (1950X,msys2 / GCC 8.3.0,Win10)

我不知道这到底是哪种优化,要应用什么条件必须满足。没有explicitly listed for -O3个选项(-O2 +全部都不够)。显然,当循环计数器使用std::size_t而不是int时,它还没有这样做。