memcpy
を使用してパフォーマンスを向上させるにはどうすればよいですか、それをどのように活用すればよいですか?たとえば :これより速いC++で標準の演算子よりもmemcpyを使うべきですか?
memcpy(a, b, 3*sizeof(float));
:
float a[3]; float b[3];
は、コードのですか?
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
memcpy
を使用してパフォーマンスを向上させるにはどうすればよいですか、それをどのように活用すればよいですか?たとえば :これより速いC++で標準の演算子よりもmemcpyを使うべきですか?
memcpy(a, b, 3*sizeof(float));
:
float a[3]; float b[3];
は、コードのですか?
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
あなたの関心事ではありません。
メンテナンス可能なコードを書きます。
memcpy()が非効率的であることが非常に多くの回答からわかりました。これは、メモリのコピーブロックの最も効率的な方法(Cプログラム用)として設計されています。
だから私はテストとして次のことを書いた:
#include <algorithm>
extern float a[3];
extern float b[3];
extern void base();
int main()
{
base();
#if defined(M1)
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
#elif defined(M2)
memcpy(a, b, 3*sizeof(float));
#elif defined(M3)
std::copy(&a[0], &a[3], &b[0]);
#endif
base();
}
次にコードを比較して生成します。
g++ -O3 -S xr.cpp -o s0.s
g++ -O3 -S xr.cpp -o s1.s -DM1
g++ -O3 -S xr.cpp -o s2.s -DM2
g++ -O3 -S xr.cpp -o s3.s -DM3
echo "=======" > D
diff s0.s s1.s >> D
echo "=======" >> D
diff s0.s s2.s >> D
echo "=======" >> D
diff s0.s s3.s >> D
これがもたらした:(コメントは手作業で追加)
======= // Copy by hand
10a11,18
> movq [email protected](%rip), %rcx
> movq [email protected](%rip), %rdx
> movl (%rdx), %eax
> movl %eax, (%rcx)
> movl 4(%rdx), %eax
> movl %eax, 4(%rcx)
> movl 8(%rdx), %eax
> movl %eax, 8(%rcx)
======= // memcpy()
10a11,16
> movq [email protected](%rip), %rcx
> movq [email protected](%rip), %rdx
> movq (%rdx), %rax
> movq %rax, (%rcx)
> movl 8(%rdx), %eax
> movl %eax, 8(%rcx)
======= // std::copy()
10a11,14
> movq [email protected](%rip), %rsi
> movl $12, %edx
> movq [email protected](%rip), %rdi
> call _memmove
上記のループを実行するためのタイミング結果を1000000000
に追加しました。
g++ -c -O3 -DM1 X.cpp
g++ -O3 X.o base.o -o m1
g++ -c -O3 -DM2 X.cpp
g++ -O3 X.o base.o -o m2
g++ -c -O3 -DM3 X.cpp
g++ -O3 X.o base.o -o m3
time ./m1
real 0m2.486s
user 0m2.478s
sys 0m0.005s
time ./m2
real 0m1.859s
user 0m1.853s
sys 0m0.004s
time ./m3
real 0m1.858s
user 0m1.851s
sys 0m0.006s
+1。そして、あなたがこれから明白な結論を書き留めていないので、memcpy呼び出しは最も効率的なコードを生成しているように見えます。 –
違いはありますが、 '3 * sizeof(float)'は 'sizeof a 'でなければならないので、' a'のサイズが変わると 'memcpy'の呼び出しがそれに合わせて調整されます。 –
Huh。なぜ '_memmove'の呼び出しがインライン化されていないのですか? –
コンパイラは、具体的にmemcpy
コール、& GCCはない少なくとも打ち鳴らすを最適化します。あなたができるところならどこでもそれを好むべきです。
@ismail:コンパイラは 'memcpy'を最適化するかもしれませんが、第2のアプローチよりも速くなる可能性は低いです。 Simoneの記事をお読みください。 – Nawaz
@Nawaz:私は同意しない。 memcpy()は、アーキテクチャーのサポートにより、より高速になる可能性があります。とにかくこれはstd :: copy(@crazylammerで説明されているように)がおそらく最適な解決策であるため、冗長です。 –
おそらく、Nawaz氏によると、割り当てのバージョンは、ほとんどのプラットフォームでより速くなるはずです()。これは、memcpy()
が1バイトずつコピーし、2番目のバージョンが4バイトずつコピーできるためです。
いつものように、ボトルネックと思われるものが現実と一致するようにアプリケーションを常にプロファイルする必要があります。
同じことがダイナミックアレイに適用されます。あなたはC++に言及しているので、その場合はstd::copy()
アルゴリズムを使うべきです。
編集
これは-O3フラグを付けてコンパイルし、GCC 4.5.0でのWindows XP用のコードを出力します:
extern "C" void cpy(float* d, float* s, size_t n)
{
memcpy(d, s, sizeof(float)*n);
}
OPがあまりにも動的配列を指定したので、私はこの機能を行っています。
出力アセンブリは以下の通りです:もちろんの
_cpy:
LFB393:
pushl %ebp
LCFI0:
movl %esp, %ebp
LCFI1:
pushl %edi
LCFI2:
pushl %esi
LCFI3:
movl 8(%ebp), %eax
movl 12(%ebp), %esi
movl 16(%ebp), %ecx
sall $2, %ecx
movl %eax, %edi
rep movsb
popl %esi
LCFI4:
popl %edi
LCFI5:
leave
LCFI6:
ret
、私はここでは何rep movsb
手段を知っている専門家のすべてを想定しています。
extern "C" void cpy2(float* d, float* s, size_t n)
{
while (n > 0) {
d[n] = s[n];
n--;
}
}
次のコードが得られる:
_cpy2:
LFB394:
pushl %ebp
LCFI7:
movl %esp, %ebp
LCFI8:
pushl %ebx
LCFI9:
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
movl 16(%ebp), %eax
testl %eax, %eax
je L2
.p2align 2,,3
L5:
movl (%ecx,%eax,4), %edx
movl %edx, (%ebx,%eax,4)
decl %eax
jne L5
L2:
popl %ebx
LCFI10:
leave
LCFI11:
ret
一度に4つのバイトを移動
これは、割当のバージョンです。
@Simone:最初のパラは私にとって意味があります。今私は確信が持てないので、確認する必要があります。 :-) – Nawaz
私はmemcopyをバイト単位でコピーすると思いません。これは、特に大量のメモリを非常に効率的にコピーするように設計されています。 –
ソースをお願いしますか? POSIXが義務づけているのは、[this](http://pubs.opengroup。org/onlinepubs/9699919799/functions/memcpy.html)。ところで、[この実装](http://www.gnu.org/software/mifluz/doc/doxydoc/memcpy2_8c-source.html)がそれほど速いかどうかを見てください。 – Simone
memcpyの利点は?おそらく可読性。それ以外の場合は、いくつかの割り当てを行うか、コピーのためのforループを持たなければならないでしょう。どちらもmemcpyを行うのと同じくらい簡単ではありません(もちろん、あなたの型がシンプルで、破壊)。
また、memcpyは、一般的に特定のプラットフォームに対して比較的単純化されていますが、単純な割り当てよりもはるかに遅くないこともあります。
memcpy
は、コピーするオブジェクトに明示的なコンストラクタがない場合、つまりメンバー(いわゆるPain、 "Plain Old Data")の場合にのみ使用できます。だからの場合はmemcpy
に電話しても構いませんが、たとえばstd::string
の場合は間違っています。
std::copy
から<algorithm>
までは、組み込みタイプに特化しています(そして、他のすべてのPODタイプでも可能ですが、STL実装に依存します)。したがって、std::copy(a, a + 3, b)
を書くことは、memcpy
のように(コンパイラの最適化後の)高速ですが、エラーが起こりにくくなります。
'std :: copy'は'
このようにmemcpyを使用するなど、早すぎるマイクロ最適化は行ってはいけません。代入を使用すると、エラーが発生しにくくなり、適切なコンパイラが適切に効率的なコードを生成します。コードをプロファイリングして割り当てが重大なボトルネックになっている場合にのみ、ある種のマイクロ最適化を検討することができますが、一般的には最初のインスタンスで常に明確で堅牢なコードを記述する必要があります。
N(N> 2)個の異なる配列項目を1つの 'memcpy'よりも1つずつ明確にする方法はありますか? 'memcpy(a、b、sizeof a)'は、 'a'と' b'のサイズが変更された場合、割り当てを追加/削除する必要がないので、より明確です。 –
@Chris Lutz:コード全体の堅牢性については、生涯を通じて考える必要があります。ある時点で誰かがaの宣言を変更して配列の代わりにポインタになるとどうなりますか?この場合、割り当ては中断されませんでしたが、memcpyは失敗します。 –
'memcpy'は壊れません(' sizeof aトリックは壊れますが、一部の人だけがそれを使います)。 'std :: copy'もどちらもほとんどすべての点で両者より優れていません。 –
std::copy()
を使用してください。 g++
のヘッダーファイルとして:
このインライン関数は、可能であれば@c memmoveの呼び出しに沸きます。
おそらく、Visual Studioの違いはあまりありません。通常の方法で行って、ボトルネックを認識したら最適化してください。単純なコピーの場合、コンパイラはおそらくすでにあなたのために最適化しています。
私は、floatの代入演算子もmemcpyを使用して実装されていると思います。だから、直接配列全体のmemcpyを使用すると速くなります – Akhil
私はあなたの編集を信じていません。なぜ第2のアプローチがより速くなるのでしょうか? memcpy()は、メモリの領域をある場所から別の場所にコピーするように特別に設計されているため、基礎となるアーキテクチャが許す限り効率的でなければなりません。ブロックメモリコピーを実行するのに適切なアセンブリを使用することになるでしょう。 –