36

いつ使用してパフォーマンスを向上させることができますmemcpyか、またはそれを使用することでどのように利益を得ることができますか?例えば:

float a[3]; float b[3];

コードです:

memcpy(a, b, 3*sizeof(float));

これより速い

a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
4

7 に答える 7

63

効率はあなたの関心事ではありません。
クリーンで保守可能なコードを記述します。

非常に多くの回答が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    _a@GOTPCREL(%rip), %rcx
>   movq    _b@GOTPCREL(%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    _a@GOTPCREL(%rip), %rcx
>   movq    _b@GOTPCREL(%rip), %rdx
>   movq    (%rdx), %rax
>   movq    %rax, (%rcx)
>   movl    8(%rdx), %eax
>   movl    %eax, 8(%rcx)

=======    // std::copy()
10a11,14
>   movq    _a@GOTPCREL(%rip), %rsi
>   movl    $12, %edx
>   movq    _b@GOTPCREL(%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
于 2010-12-28T10:08:10.873 に答える
18

memcpyコピーするオブジェクトに明示的なコンストラクターがない場合、つまりそのメンバー(いわゆるPOD、「プレーンオールドデータ」)がない場合にのみ使用できます。したがって、を呼び出すことは問題ありませんが、memcpyたとえばfloat、は間違っていstd::stringます。

ただし、作業の一部はすでに完了していますstd::copy。from<algorithm>は組み込みタイプに特化しています(おそらく、他のすべてのPODタイプに特化しています。STLの実装によって異なります)。したがって、書き込みstd::copy(a, a + 3, b)は(コンパイラの最適化後)と同じくらい高速ですmemcpyが、エラーが発生しにくくなります。

于 2010-12-28T09:05:12.843 に答える
12

コンパイラは特にmemcpy呼び出しを最適化しますが、少なくともclangとgccは最適化します。ですから、できる限りそれを好むべきです。

于 2010-12-28T09:00:33.233 に答える
6

を使用しstd::copy()ます。g++ノートのヘッダーファイルとして:

このインライン関数は、可能な限り@cmemmoveの呼び出しに要約されます。

おそらく、VisualStudioのものはそれほど違いはありません。通常の方法で進み、ボトルネックに気づいたら最適化します。単純なコピーの場合、コンパイラーはおそらくすでに最適化されています。

于 2010-12-28T09:11:11.957 に答える
5

このようなmemcpyの使用など、時期尚早のマイクロ最適化は行わないでください。割り当てを使用すると、より明確でエラーが発生しにくくなり、適切なコンパイラーであれば適切に効率的なコードが生成されます。コードのプロファイルを作成し、割り当てが重大なボトルネックであることがわかった場合に限り、ある種のマイクロ最適化を検討できますが、一般に、最初に明確で堅牢なコードを作成する必要があります。

于 2010-12-28T09:07:21.507 に答える
4

memcpyのメリットは?おそらく読みやすさ。それ以外の場合は、いくつかの割り当てを行うか、コピー用のforループを作成する必要があります。どちらも、memcpyを実行するほど単純で明確ではありません(もちろん、型が単純で、構築を必要としない限り、破壊)。

また、memcpyは通常、特定のプラットフォーム向けに比較的最適化されており、単純な割り当てよりもそれほど遅くなることはなく、さらに高速になる可能性があります。

于 2010-12-28T09:04:58.553 に答える
0

おそらく、Nawazが言ったように、割り当てバージョンはほとんどのプラットフォームでより高速であるはずです。これmemcpy()は、2番目のバージョンが一度に4バイトをコピーできるのに対し、バイトごとにコピーするためです。

常にそうであるように、ボトルネックになると予想されるものが現実と一致することを確認するために、常にアプリケーションのプロファイルを作成する必要があります。

EditSame
は動的配列に適用されます。C ++について言及しているのでstd::copy()、その場合はアルゴリズムを使用する必要があります。

編集
これは、-O3フラグを使用してコンパイルされたGCC4.5.0を使用したWindowsXPのコード出力です。

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バイト移動します。

于 2010-12-28T09:00:56.170 に答える