28

したがって、次のコードでは、パラメータの型をからx( with ) に変更すると、gcc 4.7.2 と flagsでコンパイルすると、約 25% 高速化されますが、なぜこれが大きな違いになるのかわかりません。const ullconst ull&typedef unsigned long long ull-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 に答える