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

veh*_*zzz 23 c c++ optimization performance

double d[10];
int length = 10;

memset(d, length * sizeof(double), 0);

//or

for (int i = length; i--;)
  d[i] = 0.0;
Run Code Online (Sandbox Code Playgroud)

sha*_*oth 41

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

std::fill( array, array + numberOfElements, 0.0 );
Run Code Online (Sandbox Code Playgroud)

  • @hacker:Enigma在抱怨这个答案之前大约1分钟就将其重新定为C问题.这很好,但是当你考虑到这一点时,他的评论会让我感到不公正. (13认同)
  • 你们有没有看到这是AC问题而不是C++.AFAIK,C没有填充函数,更不用std命名空间 (11认同)
  • 所以我想问题的答案是"你们看到这是一个C问题",是"不,显然不是,因为这是一个C++问题".我很想改变问题,将Java数组设置为12.4,然后抱怨所有答案都无关紧要;-) (11认同)
  • IMO完美的答案._If_`std :: memset()`是可能的,我希望std lib实现调用它,如果没有,它也做正确的事情. (8认同)
  • 我发誓,它首次发布时被标记为"c ++"!(不是我更喜欢这里的`c ++`方式). (7认同)
  • `fill_n`听起来更合适. (4认同)
  • @onebyone - 不要忘记在你发布的Java代码中至少有3个错误. (4认同)
  • 布莱恩,同意这两个账户(重新做好准备,但不适合不公正的进攻). (3认同)

cod*_*nix 24

请注意,对于memset,您必须传递字节数,而不是元素数,因为这是一个旧的C函数:

memset(d, 0, sizeof(double)*length);
Run Code Online (Sandbox Code Playgroud)

memset 可以更快,因为它是用汇编语言编写的,而std::fill模板函数只是在内部循环.

但是对于类型安全和更易读的代码,我建议 std::fill() - 它是c ++的处理方式,并考虑memset代码中这个地方是否需要性能优化.

  • 对不起,有很多实现专门针对POD的std :: fill和std :: copy.因为这些特化可以假定对齐的指针,它们可能比std :: memset()更快.Memset必须处理所有边缘情况. (17认同)
  • @StephaneRolland至少在GCC中使用`-O2``std∷fill`****更快.您可以通过要求编译器生成带有`-S`选项的程序集来自行检查 - 您会看到`memset`转出来是对库函数的调用,而`std∷fill`变成了一些程序集没有"呼叫"的说明. (3认同)
  • @Hi-Angel:正如我在 OP 上评论的那样,[取决于编译选项](http://goo.gl/zNU7hQ),常量大小的 `memset` 被内联到 `rep stosq`,它会表现得非常好(可能比你从 `std::fill` 得到的循环更好,除了非常小的数组,因为它有一点启动开销)。 (2认同)

for*_*ran 12

试试这个,如果只是为了酷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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,它被称为Duff的设备:http://en.wikipedia.org/wiki/Duff%27s_device (12认同)
  • 当Tom Duff出来时,这可能让你很酷.现在它可能让其他程序员讨厌你.而且(两者)都是正确的. (4认同)
  • @frast最好从坏的xD中筛选出优秀的程序员 (4认同)
  • @sbi你无法发现讽刺是令人着迷的 (3认同)
  • memset可能更快.一些编译器在汇编中实现它,并使用缓存预取进行128位移动.不能真的击败那个. (3认同)

小智 6

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

  • 如果您使用的是IEEE-754,则可以做出这样的假设,并且很少有理由证明您的浮点代码能够容忍与IEEE标准截然不同的实现.事实上,我可以想象只能容忍不完全合规的唯一原因是你编写的gpgpu代码针对的是一个没有实现所有舍入模式和根据规范进行NaN处理的平台. (4认同)
  • @unknown:嗯,它通常是IEEE-754,但并非总是如此.因此,请确保在代码中的某处包含`BOOST_STATIC_ASSERT(numeric_limits <double> :: is_iec559);` 如果假设是错误的话,事情会很快失败. (3认同)

MSa*_*ers 5

假设循环长度是一个整数常量表达式,最好的结果是良好的优化器将识别for循环和memset(0).结果是生成的程序集基本相同.也许寄存器的选择可能不同,或者设置不同.但每双的边际成本应该是一样的.

  • 我用visual c ++测试了这个,发现"std :: fill_n(d,1000000,0.0f)"确实编译成xor edx,edx mov r8d,8000000; 007a1200H mov rcx,rax call memset所以为了效率,确实没有任何区别 (7认同)