9

XP64 を搭載した Core2 マシン (Dell T5400) があります。

32 ビット プロセスを実行している場合、memcpy のパフォーマンスは 1.2GByte/s 程度であることがわかります。ただし、64 ビット プロセスの memcpy は約 2.2GByte/s (Intel コンパイラ CRT の memcpy では 2.4GByte/s) を達成します。最初の反応は、64 ビット コードで使用できるレジスタがより広いため、これを単に説明するだけかもしれませんが、独自の memcpy のような SSE アセンブリ コード (32 ビット幅に関係なく 128 ビット幅のロード ストアを使用する必要がある) /64-bitness of the process) は、それが達成するコピー帯域幅の同様の上限を示しています。

私の質問は、この違いは実際には何によるものですか? 32 ビット プロセスは、RAM に到達するために余分な WOW64 フープをジャンプする必要がありますか? それはTLBやプリフェッチャーと関係がありますか、それとも...何ですか?

洞察をありがとう。

Intel フォーラムでも取り上げられました。

4

7 に答える 7

8

以下で説明できると思います。

メモリからレジスタにデータをコピーしてメモリに戻すには、次のようにします。

mov eax, [address]
mov [address2], eax

これにより、アドレスからアドレス 2 に 32 ビット (4 バイト) が移動します。同じことが64ビットモードの64ビットにも当てはまります

mov rax, [address]
mov [address2], rax

これにより、アドレスからアドレス 2 に 64 ビット、2 バイトが移動します。「mov」自体は、Intelのスペックによると、64bit、32bitに関わらずレイテンシ0.5、スループット0.5。レイテンシは、命令がパイプラインを通過するのに必要なクロック サイクル数であり、スループットは、同じ命令を再び受け入れる前に CPU が待機する必要がある時間です。ご覧のとおり、クロック サイクルごとに 2 つの mov を実行できますが、2 つの mov の間で半クロック サイクル待機する必要があるため、事実上クロック サイクルごとに 1 つの mov しか実行できません (または、ここで間違っていて、用語を誤解していますか?詳細はこちらのPDFをご覧ください)。

もちろん、mov reg, memデータが第 1 または第 2 レベルのキャッシュにあるか、またはキャッシュにまったくなく、メモリから取得する必要があるかによって、a は 0.5 サイクルよりも長くなる可能性があります。ただし、上記のレイテンシー時間はこの事実を無視しています (上記でリンクした PDF の状態のように)。mov に必要なすべてのデータが既に存在することを前提としています (そうでない場合、どこからでもデータをフェッチするのにかかる時間だけレイテンシーが増加します)現在-これは数クロックサイクルである可能性があり、実行されているコマンドとは完全に独立しています.482/C-30ページのPDFは述べています)。

興味深いことに、MOV が 32 ビットか 64 ビットかは関係ありません。つまり、メモリ帯域幅が制限要因にならない限り、64 ビットの mov は 32 ビットの mov と同等に高速であり、64 ビットを使用する場合、同じ量のデータを A から B に移動するのに半分の mov しかかからないため、スループットは(理論的には) 2 倍の高さになります (そうではないという事実は、おそらくメモリが無制限に高速ではないためです)。

より大きな SSE レジスターを使用すると、より高速なスループットが得られると思いますか? 私の知る限り、xmm レジスタは 256 ではなく、128 ビット幅です (ウィキペディアの参照)。しかし、レイテンシーとスループットを考慮したことはありますか? 移動するデータが 128 ビットでアラインされているかどうか。それに応じて、次のいずれかを使用して移動します

movdqa xmm1, [address]
movdqa [address2], xmm1

または整列していない場合

movdqu xmm1, [address]
movdqu [address2], xmm1

movdqa/movdqu のレイテンシは 1 で、スループットは 1 です。したがって、命令の実行には 2 倍の時間がかかり、命令の後の待機時間は通常の mov の 2 倍になります。

また、CPU が実際に命令をマイクロオペレーションに分割し、これらを並行して実行できるという事実も考慮に入れていませんでした。今、それは本当に複雑になり始めています... 私には複雑すぎます。

とにかく、私は経験から、xmm レジスタとの間でデータをロードすることは、通常のレジスタとの間でデータをロードすることよりもはるかに遅いことを知っています。結局、SSE memmove が通常の memmove よりもそれほど遅くないことに、私は実際に驚いています。

于 2008-11-06T18:28:06.117 に答える
5

私はついにこれの底にたどり着きました(そして、Die in Senteの答えは正しい行にありました、ありがとう)

以下では、dst と src は 512 MByte の std::vector です。Intel 10.1.029 コンパイラと CRT を使用しています。

どちらも64ビット

memcpy(&dst[0],&src[0],dst.size())

memcpy(&dst[0],&src[0],N)

ここで、N は以前に宣言されたconst size_t N=512*(1<<20); call

__intel_fast_memcpy

その大部分は次のもので構成されています。

  000000014004ED80  lea         rcx,[rcx+40h] 
  000000014004ED84  lea         rdx,[rdx+40h] 
  000000014004ED88  lea         r8,[r8-40h] 
  000000014004ED8C  prefetchnta [rdx+180h] 
  000000014004ED93  movdqu      xmm0,xmmword ptr [rdx-40h] 
  000000014004ED98  movdqu      xmm1,xmmword ptr [rdx-30h] 
  000000014004ED9D  cmp         r8,40h 
  000000014004EDA1  movntdq     xmmword ptr [rcx-40h],xmm0 
  000000014004EDA6  movntdq     xmmword ptr [rcx-30h],xmm1 
  000000014004EDAB  movdqu      xmm2,xmmword ptr [rdx-20h] 
  000000014004EDB0  movdqu      xmm3,xmmword ptr [rdx-10h] 
  000000014004EDB5  movntdq     xmmword ptr [rcx-20h],xmm2 
  000000014004EDBA  movntdq     xmmword ptr [rcx-10h],xmm3 
  000000014004EDBF  jge         000000014004ED80 

~2200 MByte/s で実行されます。

しかし、32ビットでは

memcpy(&dst[0],&src[0],dst.size())

通話

__intel_fast_memcpy

その大部分は

  004447A0  sub         ecx,80h 
  004447A6  movdqa      xmm0,xmmword ptr [esi] 
  004447AA  movdqa      xmm1,xmmword ptr [esi+10h] 
  004447AF  movdqa      xmmword ptr [edx],xmm0 
  004447B3  movdqa      xmmword ptr [edx+10h],xmm1 
  004447B8  movdqa      xmm2,xmmword ptr [esi+20h] 
  004447BD  movdqa      xmm3,xmmword ptr [esi+30h] 
  004447C2  movdqa      xmmword ptr [edx+20h],xmm2 
  004447C7  movdqa      xmmword ptr [edx+30h],xmm3 
  004447CC  movdqa      xmm4,xmmword ptr [esi+40h] 
  004447D1  movdqa      xmm5,xmmword ptr [esi+50h] 
  004447D6  movdqa      xmmword ptr [edx+40h],xmm4 
  004447DB  movdqa      xmmword ptr [edx+50h],xmm5 
  004447E0  movdqa      xmm6,xmmword ptr [esi+60h] 
  004447E5  movdqa      xmm7,xmmword ptr [esi+70h] 
  004447EA  add         esi,80h 
  004447F0  movdqa      xmmword ptr [edx+60h],xmm6 
  004447F5  movdqa      xmmword ptr [edx+70h],xmm7 
  004447FA  add         edx,80h 
  00444800  cmp         ecx,80h 
  00444806  jge         004447A0

~1350 MByte/s のみで動作します。

でも

memcpy(&dst[0],&src[0],N)

ここで、N は事前に宣言さconst size_t N=512*(1<<20);れており、(32 ビットで) への直接呼び出しにコンパイルされます。

__intel_VEC_memcpy

その大部分は

  0043FF40  movdqa      xmm0,xmmword ptr [esi] 
  0043FF44  movdqa      xmm1,xmmword ptr [esi+10h] 
  0043FF49  movdqa      xmm2,xmmword ptr [esi+20h] 
  0043FF4E  movdqa      xmm3,xmmword ptr [esi+30h] 
  0043FF53  movntdq     xmmword ptr [edi],xmm0 
  0043FF57  movntdq     xmmword ptr [edi+10h],xmm1 
  0043FF5C  movntdq     xmmword ptr [edi+20h],xmm2 
  0043FF61  movntdq     xmmword ptr [edi+30h],xmm3 
  0043FF66  movdqa      xmm4,xmmword ptr [esi+40h] 
  0043FF6B  movdqa      xmm5,xmmword ptr [esi+50h] 
  0043FF70  movdqa      xmm6,xmmword ptr [esi+60h] 
  0043FF75  movdqa      xmm7,xmmword ptr [esi+70h] 
  0043FF7A  movntdq     xmmword ptr [edi+40h],xmm4 
  0043FF7F  movntdq     xmmword ptr [edi+50h],xmm5 
  0043FF84  movntdq     xmmword ptr [edi+60h],xmm6 
  0043FF89  movntdq     xmmword ptr [edi+70h],xmm7 
  0043FF8E  lea         esi,[esi+80h] 
  0043FF94  lea         edi,[edi+80h] 
  0043FF9A  dec         ecx  
  0043FF9B  jne         ___intel_VEC_memcpy+244h (43FF40h) 

〜2100MByte / sで実行されます(32ビットが何らかの理由で帯域幅に制限されていないことを証明しています).

私自身の memcpy に似た SSE コードが 32 ビット ビルドで同様の ~1300 MB/制限に苦しんでいるという私の主張を撤回します。現在、32 ビットまたは 64 ビットで 2GByte/s を超える問題はありません。(上記の結果のヒントとして)秘訣は、非一時的(「ストリーミング」)ストア(_mm_stream_ps組み込みなど)を使用することです。

32 ビットのmemcpydst.size()が最終的により高速なmovntバージョンを呼び出さないのは少し奇妙に思われます " "CPUID実際のデータに近づく前に)しかし、少なくとも私は今観察された動作を理解しています(SysWow64またはH / Wに関連していません)。

于 2009-02-11T12:58:46.077 に答える
3

もちろん、デバッガーでマシンコードにステップインして、memcpy の最も内側のループ内で実行されている実際のマシン命令を確認する必要があります。それ以外はただの憶測です。

私の質問は、おそらく 32 ビット対 64 ビット自体とは何の関係もないということです。私の推測では、より高速なライブラリ ルーチンは、SSE 非テンポラル ストアを使用して作成されたものです。

内側のループに従来のロード/ストア命令のバリエーションが含まれている場合、宛先メモリをマシンのキャッシュに読み込み、変更して、書き戻す必要があります。その読み取りはまったく不要なので (読み取られるビットはすぐに上書きされます)、キャッシュをバイパスする「非一時的な」書き込み命令を使用することで、メモリ帯域幅の半分を節約できます。そうすれば、メモリへの往復ではなく、一方通行で宛先メモリが書き込まれます。

私は Intel コンパイラの CRT ライブラリを知らないので、これは単なる推測です。32 ビットの libCRT が同じことを実行できない理由は特にありませんが、引用した高速化は、movdqa 命令を movnt に変換するだけで期待できる範囲内にあります...

memcpy は計算を行わないため、常にメモリの読み書き速度に制限されます。

于 2008-11-25T20:20:05.240 に答える
1

肯定的なフィードバックをありがとう! ここで何が起こっているのかを部分的に説明できると思います。

memcpy 呼び出しのタイミングだけを考えている場合、memcpy に非テンポラル ストアを使用することは間違いなく断食です。

一方、アプリケーションのベンチマークを行っている場合、movdqa ストアには、コピー先のメモリをキャッシュに残すという利点があります。または、少なくともキャッシュに収まる部分。

したがって、ランタイム ライブラリを設計していて、memcpy を呼び出したアプリケーションが memcpy 呼び出しの直後に宛先バッファーを使用すると想定できる場合は、movdqa バージョンを提供する必要があります。これにより、movntdq バージョンに続くメモリから CPU への戻りが効果的に最適化され、呼び出しに続くすべての命令がより高速に実行されます。

しかし一方で、デスティネーション バッファがプロセッサのキャッシュに比べて大きい場合、その最適化は機能せず、movntdq バージョンの方が高速なアプリケーション ベンチマークが得られます。

したがって、memcpy というアイデアには、内部で複数のバージョンが存在することになります。デスティネーション バッファがプロセッサのキャッシュに比べて小さい場合は、movdqa を使用します。それ以外の場合は、デスティネーション バッファがプロセッサのキャッシュに比べて大きい場合は、movntdq を使用します。これが 32 ビット ライブラリで起こっていることのようです。

もちろん、これは 32 ビットと 64 ビットの違いとは何の関係もありません。

私の推測では、64 ビット ライブラリは成熟していません。開発者は、そのバージョンのライブラリで両方のルーチンを提供することにまだ慣れていません。

于 2009-02-12T05:16:04.617 に答える
1

私の推測では、64 ビット プロセスは、メモリ バスの使用を最適化するプロセッサのネイティブ 64 ビット メモリ サイズを使用しているということです。

于 2008-11-06T17:00:50.350 に答える
0

目の前に参考文献がないので、タイミングや指示については絶対に前向きではありませんが、それでも理論を述べることはできます。32ビットモードでメモリ移動を行う場合は、クロックサイクルごとに単一の32ビット値を移動する「repmovsd」のような操作を行います。64ビットモードでは、クロックサイクルごとに単一の64ビット移動を実行する「repmovsq」を実行できます。この命令は32ビットコードでは使用できないため、実行速度の半分で2 x rep movsd(1サイクルあたり)を実行することになります。

非常に単純化されており、すべてのメモリ帯域幅/アライメントの問題などを無視していますが、ここからすべてが始まります...

于 2008-11-11T17:43:31.963 に答える
0

これは、64 ビット アーキテクチャ専用の memcpy ルーチンの例です。

void uint8copy(void *dest, void *src, size_t n){
    uint64_t * ss = (uint64_t)src;
    uint64_t * dd = (uint64_t)dest;
    n = n * sizeof(uint8_t)/sizeof(uint64_t); 

    while(n--)
        *dd++ = *ss++;
}//end uint8copy()

記事全文はこちら: http://www.godlikemouse.com/2008/03/04/optimizing-memcpy-routines/

于 2010-12-17T23:40:38.687 に答える