質問

C ++でmemcpy()の高速な代替手段はありますか?

役に立ちましたか?

解決

ほとんどありません。コンパイラ/標準ライブラリには、memcpyの非常に効率的でカスタマイズされた実装が含まれている可能性があります。 memcpyは基本的に、メモリの一部を別の部分にコピーするための最も低いAPIです。

さらに高速化する場合は、メモリのコピーを必要としない方法を見つけてください。

他のヒント

まず、アドバイスの言葉。標準ライブラリを書いた人は愚かではないと仮定します。 一般的なmemcpyを実装するより速い方法があれば、彼らはそれをしていたでしょう。

2番目、はい、より良い代替手段があります。

  • C ++では、 std :: copy 関数を使用します。同じことを行いますが、1)より安全であり、2)場合によってはより高速です。これはテンプレートです。つまり、特定のタイプに特化できるため、一般的なC memcpyよりも高速になる可能性があります。
  • または、あなたの特定の状況に関する優れた知識を使用できます。 memcpyの実装者は、すべてのケースでうまく機能するようにそれを書かなければなりませんでした。必要な状況に関する特定の情報がある場合は、より高速なバージョンを作成できる場合があります。たとえば、コピーするのにどれくらいのメモリが必要ですか?どのように調整されていますか?これにより、この特定のケースに対してより効率的なmemcpyを作成できる場合があります。ただし、他のほとんどの場合はうまくいきません(まったく機能する場合)

最適化の専門家Agner Fogは、最適化されたメモリ関数を公開しました: http://agner.org/optimize/#asmlib 。ただし、GPLの下です。

しばらく前に、Agnerは、これらの関数はGCCビルトインに代わるべきだと言っていました。 それ以降に行われたかどうかはわかりません。

非常に類似した質問( memset()について)に対するこの回答は、ここにも当てはまります。

基本的に、コンパイラは memcpy() / memset()に最適なコードを生成し、オブジェクトの性質(サイズ、配置など)。

C ++では memcpy() PODのみです。

高速メモリコピールーチンを検索または作成するには、プロセッサの動作を理解する必要があります。

Intel Pentium Pro以降のプロセッサは、“アウトオブオーダー実行”を実行します。命令に依存関係がない場合、多くの命令を並行して実行できます。ただし、これは、命令がレジスタのみで動作する場合のみです。メモリで動作する場合は、“ロードユニット”と呼ばれる追加のCPUユニットが使用されます(メモリからデータを読み取るため)および“ストアユニット” (メモリにデータを書き込むため)。ほとんどのCPUには2つのロードユニットと1つのストアユニットがあります。つまり、メモリから読み取る2つの命令とメモリに書き込む1つの命令を並行して実行できます(再度、相互に影響しない場合)。これらのユニットのサイズは通常、最大レジスタサイズと同じです。 CPUにXMMレジスタ(SSE)がある場合– YMMレジスター(AVX)がある場合は16バイトです– 32バイトなどです。メモリの読み取りまたは書き込みを行うすべての命令は、マイクロオペレーション(マイクロオペレーション)に変換されます。マイクロオペレーションは、マイクロオペレーションの共通プールに移動し、ロードユニットとストアユニットがそれらを処理できるまで待機します。単一のロードまたはストアユニットは、ロードまたはストアする必要があるデータサイズ(1バイトまたは32バイト)に関係なく、一度に1つのマイクロオペレーションのみを処理できます。

したがって、最速のメモリコピーは、最大サイズのレジスタ間で移動します。 AVX対応プロセッサの場合、メモリをコピーする最速の方法は、次のシーケンスをループ展開して繰り返すことです。

vmovdqa     ymm0,ymmword ptr [rcx]
vmovdqa     ymm1,ymmword ptr [rcx+20h]
vmovdqa     ymmword ptr [rdx],ymm0
vmovdqa     ymmword ptr [rdx+20h],ymm1

以前に投稿されたhplbshのGoogleコードは、8 xmmのレジスタをすべて使用してデータを書き戻す前に保持するため、あまり必要ではありませんが、あまり良くありません–ロードユニットが2つとストアユニットが1つしかないためです。したがって、2つのレジスタだけが最良の結果をもたらします。その数のレジスタを使用してもパフォーマンスは向上しません。

メモリコピールーチンでは、「高度な」機能を使用することもできます。 “ prefetch”などの手法事前にメモリをキャッシュにロードし、“非一時的な書き込み” (非常に大きなメモリチャンクをコピーしていて、出力バッファからのデータをすぐに読み取る必要がない場合)、位置合わせされた書き込みと位置合わせされていない書き込みなど

CPUIDにERMSビットがある場合、2013年以降にリリースされた最新のプロセッサには、いわゆる“ enhanced rep movsb”があるため、大容量メモリコピーの場合は“ rep movsb&#8221 ;使用することができます–コピーは非常に高速で、ymmレジスタを使用した場合よりも高速であり、キャッシュでも適切に動作します。ただし、この命令の起動コストは非常に高くなります–約35サイクルなので、大きなメモリブロックでのみ支払います。

これで、ケースに必要な最適なメモリコピールーチンを選択または作成できるようになります。

標準のmemcpy / memmoveを保持することもできますが、必要に応じて独自の特別なlargememcpy()を取得できます。

何をしようとしているかに依存します...それが十分に大きなmemcpyで、コピーへの書き込みがまばらである場合、MMAP_PRIVATEを使用したmmapを使用してコピーオンライトマッピングを作成すると、おそらく高速になる可能性があります。

プラットフォームによっては、ソースと宛先がキャッシュラインに揃えられ、サイズがキャッシュラインサイズの整数倍であることがわかっている場合など、特定のユースケースがあります。一般的に、ほとんどのコンパイラはmemcpyに最適なコードを生成します。

デフォルトのmemcpyを使用することが常に最良の選択肢であるかどうかはわかりません。私が見てきたほとんどのmemcpy実装は、最初にデータを整列しようと試み、その後、整列コピーを行う傾向があります。データがすでに整列されているか、非常に小さい場合、これは時間の無駄です。

キャッシュに悪影響を与えない限り、特殊なワードコピー、ハーフワードコピー、バイトコピーmemcpyを使用することが有益な場合があります。

また、実際の割り当てアルゴリズムをより細かく制御したい場合があります。ゲーム業界では、ツールチェーン開発者が最初に開発に費やした労力に関係なく、人々が独自のメモリ割り当てルーチンを書くことは非常に一般的です。私が見たゲームはほとんど常に Doug LeaのMalloc を使用する傾向があります。

一般的に言えば、アプリケーションのコードを簡単に高速化できるため、memcpyの最適化に時間を浪費することになります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top