T = int、unsigned int、long long int、または unsigned long long intがあると仮定すると、T myarray[100]
すべてのコンテンツをゼロにリセットする最速の方法は何ですか (初期化のためだけでなく、私のプログラムでコンテンツを数回リセットするため) ? 多分memsetで?
のような動的配列についても同じ質問ですT *myarray = new T[100]
。
memset
(from <string.h>
) は、通常、アセンブリで直接記述され、手動で最適化されたルーチンであるため、おそらく最速の標準的な方法です。
memset(myarray, 0, sizeof(myarray)); // for automatically-allocated arrays
memset(myarray, 0, N*sizeof(*myarray)); // for heap-allocated arrays, where N is the number of elements
ところで、C++ ではstd::fill
(from <algorithm>
) を使用するのが慣用的な方法です。
std::fill(myarray, myarray+N, 0);
に自動的に最適化される場合memset
があります。memset
sと同じくらい速く動作することは間違いint
ありませんが、オプティマイザーが十分に賢くない場合、小さな型の場合はパフォーマンスがわずかに低下する可能性があります。それでも、疑わしい場合は、プロファイルします。
この質問はかなり古いですが、最も慣用的な方法や、最も少ない行数で記述できる方法ではなく、最速の方法を求めているため、いくつかのベンチマークが必要です。そして、実際のテストなしにその質問に答えるのはばかげています。そこで、memset と std::fill と AnT の答えの ZERO と、AVX 組み込み関数を使用して作成したソリューションの 4 つのソリューションを比較しました。
このソリューションは一般的なものではないことに注意してください。32 ビットまたは 64 ビットのデータでのみ機能します。このコードが間違っている場合はコメントしてください。
#include<immintrin.h>
#define intrin_ZERO(a,n){\
size_t x = 0;\
const size_t inc = 32 / sizeof(*(a));/*size of 256 bit register over size of variable*/\
for (;x < n-inc;x+=inc)\
_mm256_storeu_ps((float *)((a)+x),_mm256_setzero_ps());\
if(4 == sizeof(*(a))){\
switch(n-x){\
case 3:\
(a)[x] = 0;x++;\
case 2:\
_mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
case 1:\
(a)[x] = 0;\
break;\
case 0:\
break;\
};\
}\
else if(8 == sizeof(*(a))){\
switch(n-x){\
case 7:\
(a)[x] = 0;x++;\
case 6:\
(a)[x] = 0;x++;\
case 5:\
(a)[x] = 0;x++;\
case 4:\
_mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
case 3:\
(a)[x] = 0;x++;\
case 2:\
((long long *)(a))[x] = 0;break;\
case 1:\
(a)[x] = 0;\
break;\
case 0:\
break;\
};\
}\
}
私は低レベルの最適化の専門家ではないので、これが最速の方法であると主張するつもりはありません。むしろ、memset よりも高速なアーキテクチャ依存の正しい実装の例です。
では、結果をどうぞ。静的および動的に割り当てられたサイズ 100 の int および long long 配列のパフォーマンスを計算しましたが、静的配列でデッド コードの除去を行った msvc を除いて、結果は非常に匹敵するものだったので、動的配列のパフォーマンスのみを示します。time.h の低精度クロック関数を使用して、100 万回の繰り返しの時間マーキングはミリ秒です。
clang 3.8 (clang-cl フロントエンドを使用、最適化フラグ = /OX /arch:AVX /Oi /Ot)
int:
memset: 99
fill: 97
ZERO: 98
intrin_ZERO: 90
long long:
memset: 285
fill: 286
ZERO: 285
intrin_ZERO: 188
gcc 5.1.0 (最適化フラグ: -O3 -march=native -mtune=native -mavx):
int:
memset: 268
fill: 268
ZERO: 268
intrin_ZERO: 91
long long:
memset: 402
fill: 399
ZERO: 400
intrin_ZERO: 185
msvc 2015 (最適化フラグ: /OX /arch:AVX /Oi /Ot):
int
memset: 196
fill: 613
ZERO: 221
intrin_ZERO: 95
long long:
memset: 273
fill: 559
ZERO: 376
intrin_ZERO: 188
ここで多くの興味深いことが起こっています: llvm kill gcc、MSVC の典型的なむらのある最適化 (静的配列で印象的なデッド コードの除去を行い、fill のパフォーマンスがひどい)。私の実装はかなり高速ですが、これは、ビット クリアのオーバーヘッドが他のどの設定操作よりもはるかに少ないことが認識されているためかもしれません。
Clang の実装は、大幅に高速であるため、さらに検討する価値があります。いくつかの追加テストは、その memset が実際にはゼロに特化していることを示しています。400 バイト配列の非ゼロ memset ははるかに遅く (~220ms)、gcc に匹敵します。ただし、800 バイトの配列を使用したゼロ以外の memsetting では速度に違いはありません。おそらく、その場合、memset のパフォーマンスが私の実装よりも悪いのはそのためです。特殊化は小さな配列のみを対象としており、カットオフは約 800 バイトです。また、gcc の 'fill' と 'ZERO' は memset に最適化されていない (生成されたコードを見て) ことに注意してください。gcc は単に同一のパフォーマンス特性を持つコードを生成しているだけです。
結論: memset は、このタスクに対して実際には最適化されておらず、人々はそのふりをするでしょう (そうでなければ、gcc と msvc と llvm の memset は同じパフォーマンスを発揮します)。パフォーマンスが重要な場合、memset は最終的な解決策にはなりません。特に、これらの扱いにくい中規模の配列の場合は、ビット クリアに特化しておらず、コンパイラが単独で実行できるよりも手動で最適化されていないためです。
からmemset()
:
memset(myarray, 0, sizeof(myarray));
sizeof(myarray)
のサイズがmyarray
コンパイル時にわかっている場合に使用できます。それ以外の場合、malloc
またはを介して取得するなど、動的にサイズ変更される配列を使用している場合new
は、長さを追跡する必要があります。
静的宣言には、次のものを使用できると思います。
T myarray[100] = {0};
動的宣言についても、同じ方法を提案します。memset
zero(myarray);
C++ で必要なのはこれだけです。
これをヘッダーに追加するだけです:
template<typename T, size_t SIZE> inline void zero(T(&arr)[SIZE]){
memset(arr, 0, SIZE*sizeof(T));
}
私が使用する関数は次のとおりです。
template<typename T>
static void setValue(T arr[], size_t length, const T& val)
{
std::fill(arr, arr + length, val);
}
template<typename T, size_t N>
static void setValue(T (&arr)[N], const T& val)
{
std::fill(arr, arr + N, val);
}
次のように呼び出すことができます。
//fixed arrays
int a[10];
setValue(a, 0);
//dynamic arrays
int *d = new int[length];
setValue(d, length, 0);
上記は、memset を使用するよりも C++11 の方法です。また、サイズを指定して動的配列を使用すると、コンパイル時エラーが発生します。