2015-09-03 22 views
12

これらの2つのコードスニペットは同じことを行います:2つの浮動小数点配列を一緒に追加し、それらに結果を戻すこと。プレーンC++コードは、インラインアセンブラよりも10倍高速です。どうして?

インラインアセンブラ:

void vecAdd_SSE(float* v1, float* v2) { 
    _asm { 
     mov esi, v1 
     mov edi, v2 
     movups xmm0, [esi] 
     movups xmm1, [edi] 
     addps xmm0, xmm1 
     movups [esi], xmm0 
     movups [edi], xmm0 
    } 
} 

平野C++コード:C++コードのための

void vecAdd_Std(float* v1, float* v2) { 
    v1[0] = v1[0]+ v2[0]; 
    v1[1] = v1[1]+ v2[1]; 
    v1[2] = v1[2]+ v2[2]; 
    v1[3] = v1[3]+ v2[3]; 

    v2[0] = v1[0]; 
    v2[1] = v1[1]; 
    v2[2] = v1[2]; 
    v2[3] = v1[3]; 
} 

分解(私はいくつかの理由でリリースモードで逆アセンブルを表示することができないので、分解は、デバッグモードで作られた):

void vecAdd_Std(float* v1, float* v2) { 
push  ebp 
mov   ebp,esp 
sub   esp,0C0h 
push  ebx 
push  esi 
push  edi 
lea   edi,[ebp-0C0h] 
mov   ecx,30h 
mov   eax,0CCCCCCCCh 
rep stos dword ptr es:[edi] 

    v1[0] = v1[0]+ v2[0]; 
mov   eax,4 
imul  ecx,eax,0 
mov   edx,4 
imul  eax,edx,0 
mov   edx,dword ptr [v1] 
mov   esi,dword ptr [v2] 
movss  xmm0,dword ptr [edx+ecx] 
addss  xmm0,dword ptr [esi+eax] 
mov   eax,4 
imul  ecx,eax,0 
mov   edx,dword ptr [v1] 
movss  dword ptr [edx+ecx],xmm0 
    v1[1] = v1[1]+ v2[1]; 
mov   eax,4 
shl   eax,0 
    v1[1] = v1[1]+ v2[1]; 
mov   ecx,4 
shl   ecx,0 
mov   edx,dword ptr [v1] 
mov   esi,dword ptr [v2] 
movss  xmm0,dword ptr [edx+eax] 
addss  xmm0,dword ptr [esi+ecx] 
mov   eax,4 
shl   eax,0 
mov   ecx,dword ptr [v1] 
movss  dword ptr [ecx+eax],xmm0 
    v1[2] = v1[2]+ v2[2]; 
mov   eax,4 
shl   eax,1 
mov   ecx,4 
shl   ecx,1 
mov   edx,dword ptr [v1] 
mov   esi,dword ptr [v2] 
movss  xmm0,dword ptr [edx+eax] 
addss  xmm0,dword ptr [esi+ecx] 
mov   eax,4 
shl   eax,1 
mov   ecx,dword ptr [v1] 
movss  dword ptr [ecx+eax],xmm0 
    v1[3] = v1[3]+ v2[3]; 
mov   eax,4 
imul  ecx,eax,3 
mov   edx,4 
imul  eax,edx,3 
mov   edx,dword ptr [v1] 
mov   esi,dword ptr [v2] 
movss  xmm0,dword ptr [edx+ecx] 
addss  xmm0,dword ptr [esi+eax] 
mov   eax,4 
imul  ecx,eax,3 
mov   edx,dword ptr [v1] 
movss  dword ptr [edx+ecx],xmm0 

    v2[0] = v1[0]; 
mov   eax,4 
imul  ecx,eax,0 
mov   edx,4 
imul  eax,edx,0 
mov   edx,dword ptr [v2] 
mov   esi,dword ptr [v1] 
mov   ecx,dword ptr [esi+ecx] 
mov   dword ptr [edx+eax],ecx 
    v2[1] = v1[1]; 
mov   eax,4 
shl   eax,0 
mov   ecx,4 
shl   ecx,0 
mov   edx,dword ptr [v2] 
mov   esi,dword ptr [v1] 
mov   eax,dword ptr [esi+eax] 
mov   dword ptr [edx+ecx],eax 
    v2[2] = v1[2]; 
mov   eax,4 
shl   eax,1 
mov   ecx,4 
shl   ecx,1 
mov   edx,dword ptr [v2] 
mov   esi,dword ptr [v1] 
mov   eax,dword ptr [esi+eax] 
mov   dword ptr [edx+ecx],eax 
    v2[3] = v1[3]; 
mov   eax,4 
imul  ecx,eax,3 
mov   edx,4 
imul  eax,edx,3 
mov   edx,dword ptr [v2] 
mov   esi,dword ptr [v1] 
mov   ecx,dword ptr [esi+ecx] 
mov   dword ptr [edx+eax],ecx 

} 

今、私はそれらの機能について時間測定を行い、インラインeアセンブラコードは約10倍長くなります(リリースモード)。 なぜ誰が知っていますか?私のマシン上で

+3

比較のためにC++コードの逆アセンブリを表示できますか? – Erik

+1

また、使用しているコンパイラも指定します。 (VC++のように見えますか?) –

+0

私はVC++を使用しました2015 – Philinator

答えて

19

(VS2015 64ビットモード)、コンパイラのインラインvecAdd_Stdとあなたが本当にを実行関数を呼び出していない

00007FF625921C8F vmovups  xmm1,xmmword ptr [[email protected] (07FF625929D60h)] 
00007FF625921C97 vmovups  xmm4,xmm1 
00007FF625921C9B vcvtss2sd xmm1,xmm1,xmm4 

テストコード

int main() { 
    float x[4] = {1.0, 2.0, 3.0, 4.0}; 
    float y[4] = {1.0, 2.0, 3.0, 4.0}; 

    vecAdd_Std(x, y); 

    std::cout << x[0]; 
} 
+0

それは私の質問に答える。そして今、なぜこの関数でブレークポイントを設定できないのかは明らかです。あなたの答えに感謝します。 – Philinator

+1

それは浮気で、同じフロートベクトルを2回使用して1つのメモリ負荷を取り除いています:) –

+4

@Cross_ - これは「公正なベンチマーク」ではありませんでしたが、Philinatorの解体は、コンパイラはリリースモードで生成します。そして、 "手に最適化された"アセンブリは、自動的にあなたが得ることができる最高のコードではありません。 –

5

を生成SSE命令、そうですか? xmmレジスタの設定に伴う些細なオーバヘッドはありません。メモリからレジスタに値をコピーして戻します。実際の計算よりもはるかに時間がかかります。

私はすべてのコンパイラは、関数のC++バージョンをインライン化することを見つけても驚かないだろうが、(本当にすることはできません)インラインアセンブリが含まれている機能のために同じことをしていません。

関連する問題