5

このVC++からの分解では、関数呼び出しが行われています。コンパイラは、ローカルポインタをプッシュする前にレジスタにMOVします。

    memcpy( nodeNewLocation, pNode, sizeCurrentNode );
0041A5DA 8B 45 F8             mov         eax,dword ptr [ebp-8]  
0041A5DD 50                   push        eax  
0041A5DE 8B 4D 0C             mov         ecx,dword ptr [ebp+0Ch]  
0041A5E1 51                   push        ecx  
0041A5E2 8B 55 D4             mov         edx,dword ptr [ebp-2Ch]  
0041A5E5 52                   push        edx  
0041A5E6 E8 67 92 FF FF       call        00413852  
0041A5EB 83 C4 0C             add         esp,0Ch 

直接プッシュしてみませんか?すなわち

push  dword ptr [ebp-8]

また、別のプッシュを行う場合は、手動で行ってみませんか。つまり、上記の「push eax」を実行する代わりに、

mov [esp], eax

など、これの利点は、3回の移動を行った後、プッシュで3回暗黙的に減算する代わりに、1回の減算を実行して新しいスタックポインタを設定できることです。

更新---リリースバージョン

これは、リリース用にコンパイルされたものと同じコードです。

; 741  :    memcpy( nodeNewLocation, pNode, sizeCurrentNode );

  00087 8b 45 f8     mov     eax, DWORD PTR _sizeCurrentNode$[ebp]
  0008a 8b 7b 04     mov     edi, DWORD PTR [ebx+4]
  0008d 50       push    eax
  0008e 56       push    esi
  0008f 57       push    edi
  00090 e8 00 00 00 00   call    _memcpy
  00095 83 c4 0c     add     esp, 12            ; 0000000cH

デバッグバージョンよりも間違いなく効率的ですが、それでもMOV/PUSHコンボを実行しています。

4

3 に答える 3

5

これは最適化です。これは、Intelプロセッサのマニュアル、ボリューム4、セクション12.3.3.6で明示的に言及されています。

Intel Atomマイクロアーキテクチャーでは、PUSH / POP命令を使用してスタックスペースを管理し、関数呼び出し/リターン間のアドレス調整を使用する方が、ENTER/LEAVEの代替手段を使用するよりも最適です。これは、PUSH / POPがMSROMフローを必要とせず、スタックポインタアドレスの更新がAGUで行われるためです。呼び出し先関数が呼び出し元に戻る必要がある場合、呼び出し先はPOP命令を発行して、データを復元し、EBPからスタックポインターを復元できます。

アセンブリ/コンパイラコーディングルール19。(MHの影響、Mの一般性)Intel Atomプロセッサの場合、PUSH / POPのレジスタ形式を優先し、LEAVEの使用を避けます。ADD/SUBの代わりにLEAを使用してESPを調整します。

マニュアルの残りの部分では、その理由については明確ではありませんが、暗黙的なESP調整で3サイクルのAGUストールが発生する可能性については言及されています。

于 2012-10-29T16:44:00.267 に答える
1

デバッグビルドでのみ、またはパイプライン処理やその他の考慮事項によって保証されている状況でのみ実行されると思います(たとえばesi、呼び出し後にパラメーターを挿入して使用する可能性があります)。私はいくつかのバイナリを調べましたが、MSVCは間違いなくそのようなプッシュを使用します:

 push ebx          ; mthd
 push dword ptr [ebp+place+4]
 push dword ptr [ebp+place] ; pos
 push [ebp+filedes]   ; fh
 call __lseeki64_nolock

(CRTからのコード)

2番目の質問に関しては、命令のアドレス指定espはプッシュよりも長くなります。"push eax"は1バイトで、"mov [esp-8], eax"4バイトです。実際、このアプローチ(movの代わりに)は、2、3バージョン前(オプション)pushからデフォルトでGCCによって使用されており、コードサイズが著しく増加しています。おそらくそれはコードをより速くしますが、私は確信していません。-maccumulate-outgoing-args

于 2012-10-29T16:24:49.427 に答える
1

私は実際にその理由を理解しました。これは、PentiumMMXで命令がパイプライン化される方法と関係があります。UとVの2つのパイプラインがあり、ペアリング可能な場合、MMXプロセッサが一度に2つの命令を処理できるようにします。PUSHは相互にペアリングできませんが、MOVとペアリングできます。だから、あなたが書くなら:

mov eax, [indirect]
mov esi, [indirect]
push eax
push esi

次に、命令#1と#3がペアになり、#2と#4がペアになるため、これらの4つの命令は、実質的に、単一のmov / pushと同じサイクル数で実行され、単一のmov/pushは次のようになります。 2プッシュ[間接]よりも高速です。この正確なケースについては、セクション4.3、p。41、Agner Fogによるマイクロアーキテクチャ最適化ガイドの例4.11aおよび4.11bは、インターネットで広く入手できます。

于 2012-10-29T20:00:19.033 に答える