double d[10];
int length = 10;
memset(d, length * sizeof(double), 0);
//or
for (int i = length; i--;)
d[i] = 0.0;
Memsetの場合、これは古いC関数であるため、要素の数ではなく、バイト数を渡す必要があることに注意してください。
_memset(d, 0, sizeof(double)*length);
_
memset canアセンブラで記述されているため高速ですが、_std::fill
_は単に内部でループを実行するテンプレート関数です。
しかし、型の安全性とより読みやすいコードのために私はお勧めしますstd::fill()
-それは物事を行うc ++の方法であり、パフォーマンスの最適化が必要な場合はmemset
を検討してください。コード内のこの場所。
あなたが本当に気にかけているなら、あなたは試して測定するべきです。ただし、最も移植性の高い方法は、std :: fill()を使用することです。
std::fill( array, array + numberOfElements, 0.0 );
コード内のいくつかのバグと脱落に加えて、memsetの使用は移植性がありません。すべてゼロビットのdoubleが0.0に等しいと想定することはできません。最初にコードを正しくしてから、最適化について心配してください。
ループの長さが整数の定数式であると仮定すると、最も可能性の高い結果は、優れたオプティマイザーがforループとmemset(0)の両方を認識することです。その結果、生成されたアセンブリは基本的に等しくなります。おそらく、レジスタの選択や設定が異なる可能性があります。しかし、ダブルあたりの限界費用は実際には同じであるはずです。
memset(d,0,10*sizeof(*d));
より速くなる可能性があります。彼らが言うようにあなたもできる
std::fill_n(d,10,0.);
しかし、それはおそらくループを行うためのよりきれいな方法です。
calloc(length, sizeof(double))
IEEE-754によると、正のゼロのビット表現はすべてゼロビットであり、IEEE-754準拠を要求することに何の問題もありません。 (アレイを再利用するためにゼロにする必要がある場合は、上記の解決策のいずれかを選択してください)。
IEEE 754-1975 64ビット浮動小数点 に関するこのウィキペディアの記事によると、すべて0のビットパターンは実際にdoubleを0.0に適切に初期化します。残念ながら、memsetコードはそれを行いません。
使用する必要のあるコードは次のとおりです。
_memset(d, 0, length * sizeof(double));
_
より完全なパッケージの一部として...
_{
double *d;
int length = 10;
d = malloc(sizeof(d[0]) * length);
memset(d, 0, length * sizeof(d[0]));
}
_
もちろん、mallocの戻り値に対して実行する必要があるエラーチェックは削除されます。 sizeof(d[0])
はsizeof(double)
よりもわずかに優れています。これは、dのタイプの変更に対して堅牢であるためです。
また、calloc(length, sizeof(d[0]))
を使用すると、メモリがクリアされ、後続のmemsetは不要になります。例では使用しませんでした。質問に答えられないようです。
配列にメモリを割り当てる必要があるため、この例は機能しません。これは、スタックまたはヒープで実行できます。
これはスタックでそれを行う例です:
double d[50] = {0.0};
その後はmemsetは必要ありません。
デバッグモードまたは低レベルの最適化が使用されている場合、Memsetは常に高速になります。より高いレベルの最適化でも、std :: fillまたはstd :: fill_nと同等になります。たとえば、Googleベンチマークの下の次のコードの場合:(テストセットアップ:xubuntu 18、GCC 7.3、Clang 6.0)
#include <cstring>
#include <algorithm>
#include <benchmark/benchmark.h>
double total = 0;
static void memory_memset(benchmark::State& state)
{
int ints[50000];
for (auto _ : state)
{
std::memset(ints, 0, sizeof(int) * 50000);
}
for (int counter = 0; counter != 50000; ++counter)
{
total += ints[counter];
}
}
static void memory_filln(benchmark::State& state)
{
int ints[50000];
for (auto _ : state)
{
std::fill_n(ints, 50000, 0);
}
for (int counter = 0; counter != 50000; ++counter)
{
total += ints[counter];
}
}
static void memory_fill(benchmark::State& state)
{
int ints[50000];
for (auto _ : state)
{
std::fill(std::begin(ints), std::end(ints), 0);
}
for (int counter = 0; counter != 50000; ++counter)
{
total += ints[counter];
}
}
// Register the function as a benchmark
BENCHMARK(memory_filln);
BENCHMARK(memory_fill);
BENCHMARK(memory_memset);
int main (int argc, char ** argv)
{
benchmark::Initialize (&argc, argv);
benchmark::RunSpecifiedBenchmarks ();
printf("Total = %f\n", total);
getchar();
return 0;
}
GCCのリリースモード(-O2; -march = native)で次の結果が得られます。
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
memory_filln 16488 ns 16477 ns 42460
memory_fill 16493 ns 16493 ns 42440
memory_memset 8414 ns 8408 ns 83022
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
memory_filln 87209 ns 87139 ns 8029
memory_fill 94593 ns 94533 ns 7411
memory_memset 8441 ns 8434 ns 82833
-O3の場合、または-O2の場合、次のようになります。
-----------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------
memory_filln 8437 ns 8437 ns 82799
memory_fill 8437 ns 8437 ns 82756
memory_memset 8436 ns 8436 ns 82754
TLDR:少なくともIEEE-754以外の浮動小数点ではないPODタイプの場合は、std :: fillまたはforループを絶対に使用する必要があると言われない限り、memsetを使用してください。そうしない強い理由はありません。
(注:配列の内容をカウントするforループは、clangがGoogleベンチマークループを完全に最適化しないようにするために必要です(他の方法では使用されていないことを検出します))
memset(d、10、0)は、10バイトしかヌルしないため、間違っています。意図が最も明確であるため、std :: fillを優先します。
パフォーマンスが本当に気になる場合は、適切に最適化されたforループを比較することを忘れないでください。
配列が十分に長く、接頭辞--iが接尾辞iではない場合のDuffのデバイスのいくつかの変形(ただし、ほとんどのコンパイラはおそらくそれを自動的に修正します)。
これが最適化するのに最も価値のあることかどうか疑問に思いますが。これは本当にシステムのボトルネックですか?
提案されたすべてのものの代わりとして、起動時に配列をすべてゼロに設定しないことをお勧めします。代わりに、特定のセルの値に最初にアクセスするときにのみ、値をゼロに設定します。これはあなたの質問を食い止め、より速くなるかもしれません。
一般に、memsetははるかに高速になります。長さが正しいことを確認してください。明らかに、例ではdoubleの配列が(m)割り当てられていないか定義されていません。本当にほんの一握りのdoubleで終わる場合は、ループが速くなる可能性があります。しかし、塗りつぶしループが影になるようになると、memsetは通常、速度を最大化するために、より大きく、場合によっては整列されたチャンクを使用します。
いつものように、テストして測定します。 (ただし、この場合はキャッシュに入れられ、測定値が偽物であることが判明する可能性があります)。
STLを使用しない必要がある場合...
double aValues [10];
ZeroMemory (aValues, sizeof(aValues));
ZeroMemoryは、少なくとも意図を明確にします。