1:
int a[100] = {};
2:
int a[100];
memset(a, 0, sizeof(a));
3:
int a[100];
fill(a, a + 100, 0);
上記の方法から新しい配列をゼロにする最良の方法は何ですか?それらの違いは何ですか?
int a[100] = {};
int a[100];
memset(a, 0, sizeof(a));
int a[100];
fill(a, a + 100, 0);
上記の方法から新しい配列をゼロにする最良の方法は何ですか?それらの違いは何ですか?
1: 最高。すべての値をデフォルト値に設定します。ほとんどの場合は 0 です。
2: これは危険です。パターン 0 を配列全体にコピーします。たとえば、配列が float の場合、それがゼロとして表されるという保証はありません。また、 memset はワード単位ではなくバイト単位でコピーするため、ゼロ以外のものを渡すとあらゆる種類の問題が発生する可能性があります。たとえば、memset(a, 1, ...)
で埋められ16843009
ます。C 文字列を使用している場合を除き、Memset は使用しないでください。
3: 合法で読みやすい。(1) はそうではありませんが、ゼロ以外の値に簡単に拡張できます。もっと冗長ですが。
VS2010、完全最適化を使用してパフォーマンスの問題を調査することにしました。
興味深い結果:
1:13105
2: 13044
3:4546
初期化なしの場合: 906.
したがって、VS2010 が case を使用するように見えますmemset
が1
、fill
最適化されています。
#include "stdafx.h"
#include <Windows.h>
#include <algorithm>
#include <iostream>
int fref()
{
int a[1024];
return a[512] - a[256];
}
int f1()
{
int a[1024] = {};
return a[512] - a[256];
}
int f2()
{
int a[1024];
memset(a, 0, sizeof(a));
return a[512] - a[256];
}
int f3()
{
int a[1024];
std::fill(a, a + 100, 0);
return a[512] - a[256];
}
typedef int (*Function)();
LONGLONG time(Function function)
{
const unsigned numLoops = 50000;
LARGE_INTEGER start;
QueryPerformanceCounter(&start);
for(unsigned j = 1; j != numLoops; ++j)
function();
LARGE_INTEGER end;
QueryPerformanceCounter(&end);
return end.QuadPart-start.QuadPart;
}
Function tests[]=
{
&fref, &f1, &f2, &f3
};
const unsigned numTests = sizeof(tests)/sizeof(tests[0]);
LONGLONG results[numTests] = {};
int _tmain(int argc, _TCHAR* argv[])
{
for(unsigned i = 0; i != numTests; ++i)
{
results[i] = time(tests[i]);
}
for(unsigned i = 0; i != numTests; ++i)
std::cout << results[i] << std::endl;
getchar();
return 0;
}
Keith のサンプル コードを使用します。GCC での違いは次のとおりです。
GCC 4.7.3: g++ -Wall -Wextra -std=c++0x -O3 -c array-fill.cpp
#include <algorithm>
#include <cstring>
int fref() {
int a[1024];
return a[512] - a[256]; }
int f1() {
int a[1024] = {};
return a[512] - a[256]; }
int f2() {
int a[1024];
std::memset(a, 0, sizeof(a));
return a[512] - a[256]; }
int f3() {
int a[1024];
std::fill(a, a + 100, 0);
return a[512] - a[256]; }
分解する
objdump -d array-fill.o | c++フィルター
00000000 <fref()>:
0: b8 00 10 00 00 mov $0x1000,%eax
5: e8 00 00 00 00 call a <fref()+0xa>
a: 29 c4 sub %eax,%esp
c: 8b 84 24 00 08 00 00 mov 0x800(%esp),%eax
13: 2b 84 24 00 04 00 00 sub 0x400(%esp),%eax
1a: 81 c4 00 10 00 00 add $0x1000,%esp
20: c3 ret
21: eb 0d jmp 30 <f1()>
00000030 <f1()>:
30: 31 c0 xor %eax,%eax
32: c3 ret
33: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
39: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
00000040 <f2()>:
40: b8 00 10 00 00 mov $0x1000,%eax
45: e8 00 00 00 00 call 4a <f2()+0xa>
4a: 29 c4 sub %eax,%esp
4c: 31 c0 xor %eax,%eax
4e: 81 c4 00 10 00 00 add $0x1000,%esp
54: c3 ret
55: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
59: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
00000060 <f3()>:
60: b8 00 10 00 00 mov $0x1000,%eax
65: e8 00 00 00 00 call 6a <f3()+0xa>
6a: 29 c4 sub %eax,%esp
6c: 89 e0 mov %esp,%eax
6e: 8d 94 24 90 01 00 00 lea 0x190(%esp),%edx
75: c7 00 00 00 00 00 movl $0x0,(%eax)
7b: 83 c0 04 add $0x4,%eax
7e: 39 d0 cmp %edx,%eax
80: 75 f3 jne 75 <f3()+0x15>
82: 8b 84 24 00 08 00 00 mov 0x800(%esp),%eax
89: 2b 84 24 00 04 00 00 sub 0x400(%esp),%eax
90: 81 c4 00 10 00 00 add $0x1000,%esp
96: c3 ret
この場合、 C スタイルの初期化 ( f1
) により、確実に最適化が可能になりました。