movsd を使用してコンパイラに文字をコピーさせる
-
16-09-2019 - |
質問
時間が重要な機能で、比較的短いメモリ シーケンス (1 KB 未満、通常は 2 ~ 200 バイト) をコピーしたいと考えています。CPU側でこれに最適なコードは次のようです rep movsd
. 。しかし、どういうわけかコンパイラにこのコードを生成させることができません。memcpy を使用すると、コンパイラの組み込み組み込み関数を使用してこれが行われることを期待していました (そして、そう見たことを漠然と覚えています)。しかし、逆アセンブリとデバッグに基づくと、コンパイラは代わりに memcpy/memmove ライブラリ実装への呼び出しを使用しているようです。また、コンパイラが次のループを認識して使用できるほど賢いものであることを期待していました。 rep movsd
それ自体ではありますが、そうではないようです。
char *dst;
const char *src;
// ...
for (int r=size; --r>=0; ) *dst++ = *src++;
Visual Studio コンパイラで生成する方法はありますか? rep movsd
インラインアセンブリを使用する以外のシーケンスは?
解決 3
一定サイズの memcpy の使用
その間に私が見つけたこと:
コピーされたブロック サイズがコンパイル時にわかっている場合、コンパイラは組み込みを使用します。そうでない場合は、ライブラリ実装を呼び出します。サイズがわかっている場合は、サイズに基づいて選択された、非常に優れたコードが生成されます。必要に応じて、単一の mov、movsd、または movsd の後に movsb が続く場合があります。
本当に movsb または movsd を常に使用したい場合は、「動的」サイズであっても、インライン アセンブリまたは特殊な組み込み (下記を参照) を使用する必要があるようです。サイズが「非常に短い」ことはわかっていますが、コンパイラーはそれを認識していないため、これをコンパイラーに伝えることができません。__assume(size<16) を使用しようとしたこともありますが、十分ではありません。
デモ コード、「-Ob1 (インラインのみの拡張)」でコンパイル:
#include <memory.h>
void MemCpyTest(void *tgt, const void *src, size_t size)
{
memcpy(tgt,src,size);
}
template <int size>
void MemCpyTestT(void *tgt, const void *src)
{
memcpy(tgt,src,size);
}
int main ( int argc, char **argv )
{
int src;
int dst;
MemCpyTest(&dst,&src,sizeof(dst));
MemCpyTestT<sizeof(dst)>(&dst,&src);
return 0;
}
特殊な組み込み関数
最近、movsd を使用して Visual Studio コンパイラに文字をコピーさせる非常に簡単な方法が存在することを発見しました。非常に自然でシンプルです。組み込み関数を使用します。次の組み込み関数が便利です。
他のヒント
いくつかの疑問が頭に浮かぶます。
まず、あなたは知っていますかMOVSDは速いだろうか?あなたはそのレイテンシー/スループットを見上げたことがありますか? x86アーキテクチャでは、彼らはただ現代のCPUの上で非常に効率的ではないですので、使用しないでくださいcrufty古い命令のいっぱいです。
あなたはmemcpyをするのではなく、std::copy
を使用する場合は、第二に、何が起こりますか?それは、特定のデータ型に対してコンパイル時に専門ことができるようstd::copy
は、潜在的に速くなります。
そして第三に、あなたはプロジェクトのプロパティの下に固有の機能を有効にしている - > C / C ++ - ?>最適化
もちろん、私は他の最適化も同様に有効になっていると仮定します。
あなたは最適化されたビルドを実行していますか?最適化がオンになっていない限り、それは本質的に使用しません。それはおそらく担当者MOVSDよりも優れたコピーループを使用することを指摘し、その価値も。それは時間のコピーで、64ビットを実行するために、少なくとも、MMXを試してみて、使用する必要があります。実際には6または7年前、私はこの種のものを行うためのMMX最適化されたコピーループを書きました。残念ながら、コンパイラの固有のmemcpyは約1%で、私のMMXコピーを上回りました。それは本当に、コンパイラが何をしているかについての仮定をしないように教えてくれます。
あなたはmemcpyのをタイムアウトしましたか? Visual Studioの最近のバージョンでは、memcpyの実装は、rep movsd
よりも高速である必要がありSSE2 ...を使用しています。あなたがコピーしているブロックが1キロバイトであれば、それは関数呼び出しの時間をコピーするための時間に比べて無視できる程度になりますので、本当にコンパイラは本質的に使用していないことは問題ではないのです。
movsd
を使用するために、src
32ビット境界に整列するメモリを指している必要があり、その長さが4バイトの倍数でなければならないことに注意してください。
、なぜあなたのコード使用char *
ではなくint *
か何かのでしょうか?そうでない場合は、あなたの質問は、議論の余地がある。
char *
するint *
を変更する場合は、、あなたは可能性がありますのstd::copy
から、より良い結果を得ることができます。
のmemcpyを使用してください。この問題はすでに解決されています。
FYI担当者MOVSDは常に最高ではないが、担当者のMOVSBは、いくつかの状況ではとSSEを速くすることができ、最高のようなXMM0、movntq [EDI]です。あなたも、バッファにデータを移動して、あなたの目的地に移動することで、ページの局所性を使用して、大量のメモリのためにさらに最適化することができます。