1. ホーム
  2. c++

[解決済み] memmoveはなぜmemcpyより速いのですか?

2023-01-30 06:06:27

質問

私は、時間の 50% を memmove(3) に費やすアプリケーションのパフォーマンス上のホットスポットを調査しています。 を費やしているアプリケーションのパフォーマンスのホットスポットを調査しています。このアプリケーションは、何百万もの 4 バイト整数をソートされた配列に挿入します。 を挿入し、挿入された値のスペースを確保するために memmove を使用してデータを右方向にシフトします。 挿入された値のためのスペースを確保するためです。

私の予想では、メモリのコピーは非常に速いと思っていたので、メモリに多くの時間が費やされていることに驚きました。

私の予想では、メモリのコピーは非常に高速で、memmove にこれほど多くの時間が費やされていることに驚きました。しかしその後、memmove が遅いのは、重複する領域を移動しているからではないかという考えが浮かびました。 が遅いのは、オーバーラップしている領域を移動しているためで、タイトなループで実装する必要があります。 をタイトループで実装しなければならないからだ、と思いつきました。私は、小さな とmemmoveの間に性能差があるかどうかを調べるために、小さなマイクロベンチマークを書きました。 memcpy と memmove の間に性能差があるかどうかを調べるために、私は小さなマイクロベンチマークを書きました。

2 台のマシン (core i5 と core i7) でベンチマークを実行したところ、memmove は memcpy よりも実際に高速であることがわかりました。 古い core i7 では、memcpy よりも 2 倍近く速いことさえありました。 今、私はその説明を探しています。

これは私のベンチマークです。memcpy で 100 mb をコピーし、memmove で約 100 mb を移動し、移動元と移動先は重なっています。移動元と移動先は重なっています。 を試しています。各テストは10回実行され、その平均時間が表示される。 平均時間が表示されます。

https://gist.github.com/cruppstahl/78a57cdf937bca3d062c

以下はCore i5での結果です(Linux 3.5.0-54-generic #81~precise1-Ubuntu)。 SMP x86_64 GNU/Linux、gccは4.6.3(Ubuntu/Linaro 4.6.3-1ubuntu5)です。括弧内の数字 括弧内の数字は、ソースとデスティネーション間の距離(ギャップの大きさ)です。

memcpy        0.0140074
memmove (002) 0.0106168
memmove (004) 0.01065
memmove (008) 0.0107917
memmove (016) 0.0107319
memmove (032) 0.0106724
memmove (064) 0.0106821
memmove (128) 0.0110633

MemmoveはSSE最適化アセンブラコードとして実装され、後ろから前にコピーされます。 から前へコピーします。データをキャッシュにロードするためにハードウェアプリフェッチを使用し、128 バイトを XMM レジスタにコピーし、目的地に格納します。 128 バイトを XMM レジスタにコピーし、目的地に格納します。

( memcpy-ssse3-back.S , 1650行目 ff)

L(gobble_ll_loop):
    prefetchnta -0x1c0(%rsi)
    prefetchnta -0x280(%rsi)
    prefetchnta -0x1c0(%rdi)
    prefetchnta -0x280(%rdi)
    sub $0x80, %rdx
    movdqu  -0x10(%rsi), %xmm1
    movdqu  -0x20(%rsi), %xmm2
    movdqu  -0x30(%rsi), %xmm3
    movdqu  -0x40(%rsi), %xmm4
    movdqu  -0x50(%rsi), %xmm5
    movdqu  -0x60(%rsi), %xmm6
    movdqu  -0x70(%rsi), %xmm7
    movdqu  -0x80(%rsi), %xmm8
    movdqa  %xmm1, -0x10(%rdi)
    movdqa  %xmm2, -0x20(%rdi)
    movdqa  %xmm3, -0x30(%rdi)
    movdqa  %xmm4, -0x40(%rdi)
    movdqa  %xmm5, -0x50(%rdi)
    movdqa  %xmm6, -0x60(%rdi)
    movdqa  %xmm7, -0x70(%rdi)
    movdqa  %xmm8, -0x80(%rdi)
    lea -0x80(%rsi), %rsi
    lea -0x80(%rdi), %rdi
    jae L(gobble_ll_loop)

memmoveはなぜmemcpyより速いのですか?memcpyはメモリページをコピーするものと思っています。 ループするよりもずっと速いはずです。最悪の場合、memcpyはmemmoveと同じ速さになると思います。 はmemmoveと同じ速度になると思います。

PS: 私のコードでmemmoveをmemcpyで置き換えることができないことは知っています。私は知っています。 コードサンプルは C と C++ を混合しています。この質問は、本当に学術的な目的のためだけです。 目的です。

アップデイト1

様々な回答に基づいて、いくつかのテストのバリエーションを実行しました。

  1. memcpy を 2 回実行すると、2 回目の実行が 1 回目の実行より速くなります。
  2. memcpy の宛先バッファに "touch" したとき ( memset(b2, 0, BUFFERSIZE...) ) の場合、memcpy の最初の実行も高速化されます。
  3. memcpy は memmove よりもまだ少し遅いです。

以下はその結果です。

memcpy        0.0118526
memcpy        0.0119105
memmove (002) 0.0108151
memmove (004) 0.0107122
memmove (008) 0.0107262
memmove (016) 0.0108555
memmove (032) 0.0107171
memmove (064) 0.0106437
memmove (128) 0.0106648

私の結論は、@Oliver Charlesworth のコメントに基づいて、オペレーティング システムは memcpy の宛先バッファが最初にアクセスされるとすぐに物理メモリをコミットしなければなりません (誰かがこれを "proof" する方法を知っていれば、回答を追加してください!)。さらに、@Mats Petersson が言ったように、memmove は memcpy よりもキャッシュ フレンドリーです。

すべての素晴らしい回答およびコメントに感謝します!

どのように解決するのですか?

あなたの memmove の呼び出しは、メモリを2〜128バイトずつシャッフルしています。 memcpy のコピー元とコピー先がまったく異なるのにです。 どういうわけか、これがパフォーマンスの違いを説明しています。同じ場所にコピーする場合は memcpy の方がわずかに速く終わる可能性があります。例えば ideone.com :

memmove (002) 0.0610362
memmove (004) 0.0554264
memmove (008) 0.0575859
memmove (016) 0.057326
memmove (032) 0.0583542
memmove (064) 0.0561934
memmove (128) 0.0549391
memcpy 0.0537919

すでにフォールトを起こしたインメモリページに書き戻すと、そのページには 多くの があるという証拠もありませんし、時間が半分になったわけでもありません...。 memcpy を不必要に遅くすることは問題ではないということを示しています。