28

因此,使用以下代码,将参数的类型x从更改const ullconst ull&(with typedef unsigned long long ull) 会在使用 gcc 4.7.2 和 flags 编译时产生大约 25% 的加速-O3 -std=c++11 -g,我不明白为什么这会产生如此大的差异。

static void inline single_mult(const std::vector<ull>::iterator& data,
                  const std::vector<ull>::const_iterator& rbegin,
                  const std::vector<ull>::const_iterator& rend,
                  const ull x) {
        ull tmp=0, carry=0, i=0;
        for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {
                tmp = x*(*rhs_it) + data[i] + carry;
                if (tmp >= imax) {
                        carry = tmp >> numbits;
                        tmp &= imax - 1;
                } else { 
                        carry = 0;
                }
                data[i++] = tmp;
        }
        data[i] += carry;
}

它在以下函数中调用(用于做课本长乘法)

static void naive(std::vector<ull>::iterator data, 
              std::vector<ull>::const_iterator cbegin,
              std::vector<ull>::const_iterator cend  ,
              std::vector<ull>::const_iterator rbegin,
              std::vector<ull>::const_iterator rend) {

    for (auto data_it  = cbegin;
          data_it != cend; ++data_it) {
        if (*data_it != 0) {
            single_mult(data, rbegin, rend, *data_it);
        }
        ++data;
    }
}

计时是通过调用clock()一个循环来测量它需要多长时间来完成的。不是最准确/最精确的方式,但我认为一致的 25% 差异意味着差异具有统计学意义。

完整的工作代码块:

#include <vector>
#include <limits>
#include <ctime>
#include <iostream>

typedef unsigned long long ull;
typedef unsigned int uint;
const ull numbits = (ull) std::numeric_limits<uint>::digits;        
const ull imax = 1LL << numbits;     

static void inline single_mult(const std::vector<ull>::iterator& data,
              const std::vector<ull>::const_iterator& rbegin,
              const std::vector<ull>::const_iterator& rend,
              const ull x) {
    ull tmp=0, carry=0, i=0;
    for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {
            tmp = x*(*rhs_it) + data[i] + carry;
            if (tmp >= imax) {
                    carry = tmp >> numbits;
                    tmp &= imax - 1;
            } else {
                    carry = 0;
            }
            data[i++] = tmp;
    }
    data[i] += carry;
}

static void naive(std::vector<ull>::iterator data,
              std::vector<ull>::const_iterator cbegin,
              std::vector<ull>::const_iterator cend  ,
              std::vector<ull>::const_iterator rbegin,
              std::vector<ull>::const_iterator rend) {

    for (auto data_it  = cbegin; data_it != cend; ++data_it) {
            if (*data_it != 0) {
                    single_mult(data, rbegin, rend, *data_it);
            }
    ++data;
    }
}


int main() {
    int N = 10000;
    std::vector<ull> vec(N);
    for (int i=0; i<N; ++i) {
        vec[i] = i;
    }

    auto s1 = clock();
    int num = 10;
    std::vector<ull> result(2*N);
    for (int i=0; i<num; ++i) {
    //Need to zero out the vector or the result will be different.
        std::fill(result.begin(), result.end(), 0);
        naive(result.begin(), vec.cbegin(), vec.cend(), vec.cbegin(), vec.cend());
    }
    s1 = (clock() - s1);
    std::cout << "Took " << float(s1)/CLOCKS_PER_SEC << "seconds total." << std::endl;
}

和运行时(我命名了传递值value.cpp和引用的文件reference.cpp

$ g++ -O3 -std=c++11 -g -o reference reference.cpp
$ g++ -O3 -std=c++11 -g -o value value.cpp
$ ./reference                                                                                                                                           
Took 1.05seconds total.                                                                        
$ ./value                                                                 
Took 1.83seconds total.            
4

2 回答 2

31

我能够重现您对加速的观察,这对我来说更加明显(快 1.75 倍)。问题似乎在于,当您通过值传递 x 时,它使编译器能够执行原本不会执行的优化,而这些优化适得其反,它们显然会在处理器中引入意外的停顿。编译器生成几个条件移动,背靠背,而不是比较和分支。比较和分支比背靠背条件移动运行得快得多。

我可以通过简化编译器遇到问题的代码来避免这种情况,即

if (tmp >= imax) {
    carry = tmp >> numbits;
    tmp &= imax - 1;
} else {
    carry = 0;
}

可以简化为

carry = tmp >> numbits;
tmp &= imax - 1;

这是我正在使用的 gcc 版本

g++ --version
g++ (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)

这些是我使用的命令,perf record将分析您的代码,perf report并将使用分析结果注释源代码和反汇编

 g++ -std=gnu++0x -O3 -g single_mult.cpp -o single_mult
 perf record ./single_mult
 perf report

一旦在性能报告中按下entermain选择Annotate main,您将看到程序的反汇编以及源代码以及分析器发现您的程序在函数中的每条指令处运行的时间百分比......实际上这些数字必须被视为仅作为提示,通常您会看到计数很大的指令,而实际上它是前一条指令停止或在缓存中丢失,或者存在错误预测的分支等。因此,当您看到大量计数时,请回头查看看看可能是什么原因造成的。原因是配置文件是统计的,它以恒定的速率中断您的程序并查看指令指针的位置,并且通常在处理器由于缓存未命中或错误预测的分支或某些内部原因而停止时发生中断数据依赖。

我增加了迭代次数,以便为分析器留出更多时间

int N = 20000;

当 x 按值传递时Took 11.58seconds total,这就是我所看到的,请注意cmovbe说明

       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
       :                    tmp = x*(*rhs_it) + data[i] + carry;                                                                                                                                 
 11.10 :          400b40:       4c 89 c9                mov    %r9,%rcx                                                                                                                          
  0.00 :          400b43:       48 89 fa                mov    %rdi,%rdx                                                                                                                         
  0.01 :          400b46:       48 03 14 c6             add    (%rsi,%rax,8),%rdx                                                                                                                
 11.65 :          400b4a:       48 0f af 0c c3          imul   (%rbx,%rax,8),%rcx                                                                                                                
  0.99 :          400b4f:       48 01 ca                add    %rcx,%rdx                                                                                                                         
       :                    if (tmp >= imax) {                                                                                                                                                   
       :                            carry = tmp >> numbits;                                                                                                                                      
  2.25 :          400b52:       48 89 d7                mov    %rdx,%rdi                                                                                                                         
       :                            tmp &= imax - 1;                                                                                                                                            
 10.99 :          400b55:       48 89 d1                mov    %rdx,%rcx                                                                                                                         
       :                      const ull x) {                                                                                                                                                     
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
       :                    tmp = x*(*rhs_it) + data[i] + carry;                                                                                                                                 
       :                    if (tmp >= imax) {                                                                                                                                                    
       :                            carry = tmp >> numbits;                                                                                                                                      
  0.69 :          400b58:       48 c1 ef 20             shr    $0x20,%rdi                                                                                                                        
       :                            tmp &= imax - 1;                                                                                                                                              
  9.54 :          400b5c:       83 e1 ff                and    $0xffffffff,%ecx                                                                                                                   
  9.05 :          400b5f:       4c 39 c2                cmp    %r8,%rdx                                                                                                                          
 10.78 :          400b62:       49 0f 46 fb             cmovbe %r11,%rdi                                                                                                                          
       :                    } else {                                                                                                                                                              
       :                            carry = 0;                                                                                                                                                    
       :                    }                                                                                                                                                                     
       :                    data[i++] = tmp;                                                                                                                                                     
 20.73 :          400b66:       48 83 c0 01             add    $0x1,%rax                                                                                                                          
  0.02 :          400b6a:       4c 39 c2                cmp    %r8,%rdx                                                                                                                           
  0.17 :          400b6d:       48 0f 46 ca             cmovbe %rdx,%rcx                                                                                                                         
       :        static void inline single_mult(const std::vector<ull>::iterator& data,                                                                                                            
       :                      const std::vector<ull>::const_iterator& rbegin,                                                                                                                    
       :                      const std::vector<ull>::const_iterator& rend,                                                                                                                      
       :                      const ull x) {                                                                                                                                                     
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
 11.47 :          400b71:       4c 39 d0                cmp    %r10,%rax                                                                                                                         
       :                            carry = tmp >> numbits;                                                                                                                                      
       :                            tmp &= imax - 1;                                                                                                                                             
       :                    } else {                                                                                                                                                             
       :                            carry = 0;                                                                                                                                                   
       :                    }                                                                                                                                                                    
       :                    data[i++] = tmp;                                                                                                                                                     
  0.01 :          400b74:       48 89 4c c6 f8          mov    %rcx,-0x8(%rsi,%rax,8)                                                                                                            
       :        static void inline single_mult(const std::vector<ull>::iterator& data,                                                                                                           
       :                      const std::vector<ull>::const_iterator& rbegin,                                                                                                                    
       :                      const std::vector<ull>::const_iterator& rend,                                                                                                                      
       :                      const ull x) {                                                                                                                                                     
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
  0.53 :          400b79:       75 c5                   jne    400b40 <main+0x250>                                                                                                               
       :                    } else {                                                                                                                                                             
       :                            carry = 0;                                                                                                                                                   
       :                    }                                                                                                                                                                    
       :                    data[i++] = tmp;                                                                                                                                                     
       :            }                                                                                                                                                                             
       :            data[i] += carry;                                                                                                                                                            
  0.00 :          400b7b:       4a 01 3c d6             add    %rdi,(%rsi,%r10,8)                                                                                                                
  0.01 :          400b7f:       48 83 c5 08             add    $0x8,%rbp                                                                                                                         

当 x 通过引用传递时Took 6.59seconds total,这就是我所看到的

       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
 20.90 :          400b30:       48 8b 17                mov    (%rdi),%rdx                                                                                                                       
       :                    tmp = x*(*rhs_it) + data[i] + carry;                                                                                                                                 
  1.38 :          400b33:       49 0f af 14 c1          imul   (%r9,%rax,8),%rdx                                                                                                                   
  4.82 :          400b38:       48 03 0c c6             add    (%rsi,%rax,8),%rcx                                                                                                                
 22.41 :          400b3c:       48 01 ca                add    %rcx,%rdx                                                                                                                         
       :                    if (tmp >= imax) {                                                                                                                                                   
       :                            carry = tmp >> numbits;                                                                                                                                      
       :                            tmp &= imax - 1;                                                                                                                                               
       :                    } else {                                                                                                                                                             
       :                            carry = 0;                                                                                                                                                   
  2.95 :          400b3f:       31 c9                   xor    %ecx,%ecx                                                                                                                         
       :                      const std::vector<ull>::const_iterator& rend,                                                                                                                      
       :                      const ull &x) {                                                                                                                                                    
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
       :                    tmp = x*(*rhs_it) + data[i] + carry;                                                                                                                                 
       :                    if (tmp >= imax) {                                                                                                                                                   
  0.23 :          400b41:       4c 39 d2                cmp    %r10,%rdx                                                                                                                         
  0.00 :          400b44:       76 0a                   jbe    400b50 <main+0x260>                                                                                                               
       :                            carry = tmp >> numbits;                                                                                                                                      
  2.27 :          400b46:       48 89 d1                mov    %rdx,%rcx                                                                                                                         
       :                            tmp &= imax - 1;                                                                                                                                             
  1.29 :          400b49:       83 e2 ff                and    $0xffffffff,%edx                                                                                                                  
       :                      const ull &x) {                                                                                                                                                    
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
       :                    tmp = x*(*rhs_it) + data[i] + carry;                                                                                                                                 
       :                    if (tmp >= imax) {                                                                                                                                                   
       :                            carry = tmp >> numbits;                                                                                                                                      
  0.26 :          400b4c:       48 c1 e9 20             shr    $0x20,%rcx                                                                                                                        
       :                            tmp &= imax - 1;                                                                                                                                             
       :                    } else {                                                                                                                                                             
       :                            carry = 0;                                                                                                                                                   
       :                    }                                                                                                                                                                    
       :                    data[i++] = tmp;                                                                                                                                                     
 19.67 :          400b50:       48 83 c0 01             add    $0x1,%rax                                                                                                                         
       :        static void inline single_mult(const std::vector<ull>::iterator& data,                                                                                                           
       :                      const std::vector<ull>::const_iterator& rbegin,                                                                                                                    
       :                      const std::vector<ull>::const_iterator& rend,                                                                                                                      
       :                      const ull &x) {                                                                                                                                                    
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
  0.53 :          400b54:       4c 39 c0                cmp    %r8,%rax                                                                                                                          
       :                            carry = tmp >> numbits;                                                                                                                                      
       :                            tmp &= imax - 1;                                                                                                                                             
       :                    } else {                                                                                                                                                             
       :                            carry = 0;                                                                                                                                                   
       :                    }                                                                                                                                                                      
       :                    data[i++] = tmp;                                                                                                                                                     
  0.39 :          400b57:       48 89 54 c6 f8          mov    %rdx,-0x8(%rsi,%rax,8)                                                                                                            
       :        static void inline single_mult(const std::vector<ull>::iterator& data,                                                                                                           
       :                      const std::vector<ull>::const_iterator& rbegin,                                                                                                                    
       :                      const std::vector<ull>::const_iterator& rend,                                                                                                                      
       :                      const ull &x) {                                                                                                                                                    
       :            ull tmp=0, carry=0, i=0;                                                                                                                                                     
       :            for (auto rhs_it = rbegin; rhs_it != rend; ++rhs_it) {                                                                                                                       
 22.91 :          400b5c:       75 d2                   jne    400b30 <main+0x240>                                                                                                               
       :                    } else {                                                                                                                                                             
       :                            carry = 0;                                                                                                                                                   
       :                    }                                                                                                                                                                    
       :                    data[i++] = tmp;                                                                                                                                                     
       :            }                                                                                                                                                                            
       :            data[i] += carry;                                                                                                                                                            
  0.00 :          400b5e:       4a 01 0c c6             add    %rcx,(%rsi,%r8,8)                                                                                                                 
  0.00 :          400b62:       48 83 c7 08             add    $0x8,%rdi                                                                                                                         
于 2013-02-11T20:06:40.487 回答
3

没有看到汇编代码,很难确定,但总的来说:unsigned long long按值传递可能需要编译器生成额外的代码来将值复制到堆栈上。

通过引用传递它允许编译器简单地传递指针,它可能已经在一个方便的寄存器中。

于 2013-02-11T14:12:50.430 回答