double d[10];
int length = 10;
memset(d, length * sizeof(double), 0);
//or
for (int i = length; i--;)
d[i] = 0.0;
答案 0 :(得分:40)
如果你真的在乎你应该尝试和衡量。然而,最便携的方式是使用std :: fill():
std::fill( array, array + numberOfElements, 0.0 );
答案 1 :(得分:20)
请注意,对于memset,您必须传递字节数,而不是元素数,因为这是一个旧的C函数:
memset(d, 0, sizeof(double)*length);
memset 可以更快,因为它是用汇编语言编写的,而std::fill
是一个模板函数,它只是在内部循环。
但是对于类型安全和更易读的代码我建议 std::fill()
- 这是c ++的做事方式,如果需要进行性能优化,请考虑memset
这个地方在代码中。
答案 2 :(得分:13)
试试这个,如果只是为了酷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);
}
}
答案 3 :(得分:5)
memset(d,0,10*sizeof(*d));
可能会更快。就像他们说你也可以
std::fill_n(d,10,0.);
但它很可能是一种更漂亮的循环方式。
答案 4 :(得分:5)
除了代码中的一些错误和遗漏之外,使用memset是不可移植的。您不能假设所有零位的double都等于0.0。首先让你的代码正确,然后担心优化。
答案 5 :(得分:5)
假设循环长度是一个整数常量表达式,最好的结果是良好的优化器将识别for循环和memset(0)。结果是生成的程序集基本相同。也许寄存器的选择可能不同,或者设置不同。但每双的边际成本应该是一样的。
答案 6 :(得分:3)
calloc(length, sizeof(double))
根据IEEE-754,正零的位表示都是零位,并且要求符合IEEE-754标准没有任何问题。 (如果您需要将数组清零以重复使用它,请选择上述解决方案之一)。
答案 7 :(得分:3)
根据维基百科关于IEEE 754-1975 64-bit floating point的文章,所有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。我没有在示例中使用它,因为看起来您的问题似乎无法解答。
答案 8 :(得分:3)
该示例无效,因为您必须为阵列分配内存。您可以在堆栈上或堆上执行此操作。
这是在堆栈上执行此操作的示例:
double d[50] = {0.0};
之后不需要memset。
答案 9 :(得分:2)
memset(d,10,0)错误,因为它只有10个字节。 喜欢std :: fill因为意图最清楚。
答案 10 :(得分:1)
如果您真的关心性能,请不要忘记比较经过适当优化的for循环。
Duff设备的一些变体,如果数组足够长,前缀--i不是后缀i--(尽管大多数编译器可能会自动纠正它。)。
虽然我怀疑这是否是最有价值的优化方法。这真的是系统的瓶颈吗?
答案 11 :(得分:1)
一般来说,memset会更快,确保你的长度合适,显然你的例子没有(m)分配或定义双打数组。现在,如果它真的最终只有少数双打,那么循环可能会变得更快。但是,当填充循环影响少数设置指令时,memset通常会使用较大且有时对齐的块来最大化速度。
像往常一样,测试和测量。 (虽然在这种情况下,你最终会进入缓存,但测量结果可能是假的)。
答案 12 :(得分:1)
如果使用调试模式或低级别的优化,Memset将始终更快。在更高级别的优化中,它仍然等同于std :: fill或std :: fill_n。 例如,对于Google Benchmark下的以下代码: (测试设置: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:使用memset,除非告诉您绝对必须使用std :: fill或for循环,至少对于不是非IEEE-754浮点的POD类型而言。没有充分的理由不这样做。
(注意:计数数组内容的for循环对于clang不能完全优化Google基准测试循环是必不可少的(它将检测到未使用它们))
答案 13 :(得分:0)
如果您不需要使用STL ......
double aValues [10];
ZeroMemory (aValues, sizeof(aValues));
ZeroMemory至少使意图明确。
答案 14 :(得分:0)
作为所有提议内容的替代方案,我建议你不要在启动时将数组设置为全零。而是仅在首次访问特定单元格中的值时将值设置为零。这样可以避开你的问题,可能会更快。
答案 15 :(得分:0)
严格来说,std::memset
更快,但是当您打开优化功能时,现代编译器很可能仍会使用memset进行此类for循环。
当然,根据上下文的不同,编译器可能会找到更快的选项,但是我认为典型的for循环汇编会很少见。
使用-01优化与clang 9.0组装: https://godbolt.org/z/vvnE8T
答案 16 :(得分:0)
回答此问题的一种方法是通过Compiler Explorer快速运行代码:如果选中此link,则将看到以下代码的汇编:
void do_memset(std::array<char, 1024>& a) {
memset(&a, 'q', a.size());
}
void do_fill(std::array<char, 1024>& a) {
std::fill(a.begin(), a.end(), 'q');
}
void do_loop(std::array<char, 1024>& a) {
for (int i = 0; i < a.size(); ++i) {
a[i] = 'q';
}
}
答案(至少对于clang
而言)是,在优化级别为-O0
和-O1
的情况下,程序集是不同的,并且std::fill
会更慢,因为使用迭代器尚未优化。对于-O2
及更高版本,do_memset
和do_fill
产生相同的程序集。循环最终在数组中的每个项目上调用memset
,甚至使用-O3
。
假设发行版通常可以运行-O2
或更高版本,则没有性能方面的考虑,我建议在可用的情况下使用std::fill
,在C语言中使用memset
。
答案 17 :(得分:-1)
我认为你的意思是
memset(d, 0, length * sizeof(d[0]))
和
for (int i = length; --i >= 0; ) d[i] = 0;
就个人而言,我做了其中一个,但我认为std::fill()
可能更好。