哪个更快/更首选:memset或for循环将双精度数组归零?

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 可以更快,因为它是用汇编语言编写的,而std::fill是一个模板函数,它只是在内部循环。

但是对于类型安全和更易读的代码, 我建议使用 std::fill() – 这是c ++的处理方式,如果在代码中的这个地方需要性能优化,则考虑memset

如果你真的在乎你应该尝试和衡量。 但是最便携的方法是使用std :: fill():

 std::fill( array, array + numberOfElements, 0.0 ); 

尝试这个,如果只是为了酷xD

 { double *to = d; int n=(length+7)/8; switch(length%8){ case 0: do{ *to++ = 0.0; case 7: *to++ = 0.0; case 6: *to++ = 0.0; case 5: *to++ = 0.0; case 4: *to++ = 0.0; case 3: *to++ = 0.0; case 2: *to++ = 0.0; case 1: *to++ = 0.0; }while(--n>0); } } 
 memset(d,0,10*sizeof(*d)); 

可能会更快。 就像他们说你也可以

 std::fill_n(d,10,0.); 

但它很可能是一个更漂亮的循环方式。

除了代码中的一些错误和遗漏之外,使用memset是不可移植的。 您不能假设所有零位的double都等于0.0。 首先使您的代码正确,然后担心优化。

假设循环长度是一个整数常量表达式,最好的结果是良好的优化器将识别for循环和memset(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。 我没有在示例中使用它,因为看起来你的问题似乎无法回答。

该示例无效,因为您必须为arrays分配内存。 您可以在堆栈或堆上执行此操作。

这是在堆栈上执行此操作的示例:

 double d[50] = {0.0}; 

之后不需要memset。

memset(d,10,0)是错误的,因为它只有10个字节。 更喜欢std :: fill,意图最清晰。

如果您真的关心性能,请不要忘记比较经过适当优化的for循环。

如果数组足够长,Duff设备的一些变体,前缀–i不是后缀i–(尽管大多数编译器可能会自动纠正它)。

虽然我怀疑这是否是最有价值的优化。 这真的是系统的瓶颈吗?

一般来说,memset会更快,确保你的长度合适,显然你的例子没有(m)分配或定义双打数组。 现在,如果它真的最终只有少数双打,那么循环可能会变得更快。 但是当达到填充循环阴影少数设置指令时,memset通常会使用更大且有时对齐的块来最大化速度。

像往常一样,测试和测量。 (虽然在这种情况下,你最终进入缓存,测量结果可能是虚假的)。

如果您不需要使用STL ……

 double aValues [10]; ZeroMemory (aValues, sizeof(aValues)); 

ZeroMemory至少使意图明确。

作为所有提议的替代方案,我建议你不要在启动时将数组设置为全零。 而是仅在首次访问特定单元格中的值时将值设置为零。 这将使您的问题得以解决,并且可能会更快。

如果使用调试模式或低级别的优化,Memset将始终更快。 在更高的优化级别,它仍然等同于std :: fill或std :: fill_n。 例如,对于Google Benchmark下的以下代码:(测试设置:xubuntu 18,GCC 7.3,Clang 6.0)

 #include  #include  #include  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 

以下结果是调试模式(-O0):

 ----------------------------------------------------- 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或cla在-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:使用memset,除非告诉你必须使用std :: fill或for-loop,至少对于不是非IEEE-754浮点的POD类型。 没有强烈的理由不这样做。

(注意:计算数组内容的for循环对于clang不要完全优化google基准测试循环是必要的(否则会检测到它们没有被使用))

我想你的意思是

 memset(d, 0, length * sizeof(d[0])) 

 for (int i = length; --i >= 0; ) d[i] = 0; 

就个人而言,我做任何一个,但我认为std::fill()可能更好。