memcpy を実行している関数がありますが、膨大な量のサイクルを占めています。memcpy を使用してメモリを移動するよりも高速な代替/アプローチはありますか?
17 に答える
memcpy
メモリ内でバイトをコピーできる最速の方法である可能性があります。より高速なものが必要な場合は、データ自体ではなくスワップ ポインターのみをコピーするなど、コピーしない方法を考えてみてください。
詳細をお知らせください。i386 アーキテクチャーでは、memcpy が最速のコピー方法である可能性が非常に高くなります。しかし、コンパイラが最適化されたバージョンを持っていない別のアーキテクチャでは、 memcpy 関数を書き直すのが最善です。アセンブリ言語を使用して、カスタム ARM アーキテクチャでこれを行いました。大量のメモリを転送する場合は、おそらくDMAが最適です。
詳細を提供してください - アーキテクチャ、オペレーティング システム (該当する場合)。
通常、コンパイラに同梱されている標準ライブラリはmemcpy()
、ターゲット プラットフォームで可能な最速の方法をすでに実装しています。
実際、memcpyは、特に何度も呼び出す場合、最速の方法ではありません。また、スピードアップするために本当に必要なコードがいくつかありましたが、不必要なチェックが多すぎるため、memcpyは低速です。たとえば、宛先メモリブロックとソースメモリブロックがオーバーラップしているかどうか、およびブロックの前面ではなく背面からコピーを開始する必要があるかどうかを確認します。あなたがそのような考慮事項を気にしないならば、あなたは確かにかなり良くすることができます。私はいくつかのコードを持っていますが、これはおそらくこれまで以上に優れたバージョンです:
検索すると、他の実装も見つけることができます。しかし、真の速度を得るには、アセンブリバージョンが必要です。
一般に、コピーをまったく作成しない方が高速です。関数をコピーしないように適応できるかどうかはわかりませんが、調べる価値はあります。
memcpy、memset などの関数は、2 つの異なる方法で実装されることがあります。
- 一度実関数として
- すぐにインライン化されるアセンブリとして 1 回
すべてのコンパイラがデフォルトでインライン アセンブリ バージョンを使用するわけではありません。コンパイラはデフォルトで関数バリアントを使用し、関数呼び出しのためにオーバーヘッドが発生する場合があります。関数の組み込みバリアント (コマンド ライン オプション、プラグマなど) を使用する方法については、コンパイラを確認してください。
編集: Microsoft C コンパイラの組み込み関数の説明については、 http: //msdn.microsoft.com/en-us/library/tzkfha43%28VS.80%29.aspxを参照してください。
コード用に生成されたアセンブリ コードを確認する必要があります。望ましくないのは、呼び出しで標準ライブラリmemcpy
の関数への呼び出しを生成するmemcpy
ことです。必要なのは、最適な ASM 命令を繰り返し呼び出して、最大量のデータをコピーすることですrep movsq
。
どうすればこれを達成できますか? コンパイラは、コピーする必要があるデータの量を認識している限りmemcpy
、単純な s に置き換えることでへの呼び出しを最適化します。よく決定された ( ) 値でmov
を書くと、これを見ることができます。コンパイラが値を認識しない場合、バイトレベルの実装にフォールバックする必要があります。問題は、1 バイトの粒度を尊重する必要があることです。一度に 128 ビットを移動しますが、128b ごとに、128b としてコピーするのに十分なデータがあるかどうかを確認する必要があります。または、64 ビットにフォールバックし、次に 32 ビットと 8 ビットにフォールバックする必要があります (16 ビットは最適ではない可能性があると思います)。とにかく、私は確かに知りません)。memcpy
constexpr
memcpy
memcpy
したがって、必要memcpy
なのは、コンパイラが最適化できる const 式を使用して、データのサイズを伝えることができることです。この方法では、への呼び出しmemcpy
は実行されません。望ましくないのはmemcpy
、実行時にのみ認識される変数に渡すことです。これは、最適なコピー命令をチェックするための関数呼び出しと大量のテストに変換されます。場合によっては、単純な for ループがmemcpy
この理由 (1 つの関数呼び出しを排除する) よりも優れていることがあります。そして、あなたが本当に望んでいないmemcpy
のは、コピーするために奇数バイトに渡すことです。
コンパイラ/プラットフォームのマニュアルを確認してください。一部のマイクロプロセッサおよびDSPキットでは、memcpyを使用すると、組み込み関数やDMA操作よりもはるかに低速になります。
プラットフォームがサポートしている場合は、mmap() システム コールを使用してデータをファイルに残すことができるかどうかを調べてください。通常、OS はそれをより適切に管理できます。そして、誰もが言っているように、可能な限りコピーは避けてください。このような場合、ポインターはあなたの友達です。
memcpy のパフォーマンスが問題になった場合、コピーしたい巨大なメモリ領域が必要だと思いますか?
この場合、私は何かをコピーしない方法を見つけ出すというnosの提案に同意します..
メモリを変更する必要があるたびに 1 つの巨大なメモリの塊をコピーする代わりに、代わりにいくつかの代替データ構造を試す必要があります。
問題の領域について何も知らなくても、永続的なデータ構造をよく見て、独自のものを実装するか、既存の実装を再利用することをお勧めします。
あなたはこれを見たいと思うかもしれません:
http://www.danielvik.com/2010/02/fast-memcpy-in-c.html
私が試みるもう 1 つのアイデアは、COW 手法を使用してメモリ ブロックを複製し、ページが書き込まれるとすぐに OS がオンデマンドでコピーを処理できるようにすることです。Linux でコピー オン ライト memcpy を実行できますmmap()
か?
通常、メモリからメモリへの変換は CPU のコマンド セットでサポートされており、memcpy は通常それを使用します。そして、これは通常、最速の方法です。
CPUが正確に何をしているのかを確認する必要があります。Linux では、sar -B 1 または vmstat 1 を使用するか、/proc/memstat を調べて、スワップ インとスワップ アウト、および仮想メモリの有効性を監視します。コピーを大量のページに押し出して空き領域を確保したり、ページを読み込んだりする必要がある場合があります。
つまり、問題はコピーに使用するものではなく、システムがメモリを使用する方法にあるということです。ファイルキャッシュを減らすか、早めに書き出しを開始するか、メモリ内のページをロックする必要がある場合があります。