【问题标题】:How to zero a new array?如何将新数组归零?
【发布时间】:2013-10-11 23:59:39
【问题描述】:

1:

int a[100] = {};

2:

int a[100];
memset(a, 0, sizeof(a));

3:

int a[100];
fill(a, a + 100, 0);

从上面显示的方法中将新数组归零的最佳方法是什么?它们之间有什么区别?

【问题讨论】:

  • 这真的是你想要做的,还是你想将一个从 malloc 设置的 int[] 归零?
  • std::vector<int> a(100, 0)
  • @mbratch 如果我没记错的话,第一个示例会将数组归零。
  • @mbratch,我认为您对选项 3 的看法也不对。

标签: c++


【解决方案1】:

1:最好的。它将所有值设置为其默认值,大多数情况下为 0。

2:这很危险,它将模式 0 复制到整个数组中。例如,如果数组是浮点数,则不能保证它被表示为零。 memset 也按字节复制而不是按字复制,如果您传递的不是零,这可能会导致各种问题。例如memset(a, 1, ...) 将导致它被16843009 填充。除非您使用 C 字符串,否则不应使用 Memset。

3:合法、清晰。易于扩展到非零值,而 (1) 不会。虽然比较冗长。

【讨论】:

  • 按照OP建议的方式使用std::fill是合法的。
  • 您可能会评论 b/w 1 和 3 可能的性能差异。我希望 1 有更多机会更快。
  • @Keith,我也想知道,但很难看出它是怎么回事。堆栈上的那 100 个整数必须以某种方式归零,而越过它们似乎是唯一的选择。
  • @AdamBurry 我认为编译器可能会针对这种非常标准的情况进行更好的优化;但只有分析才能说明问题。
  • @AdamBurry:您可能会这么认为,但编译器可以消除或至少延迟写入。这不是 volatile 数组。
【解决方案2】:

决定使用VS2010调查性能问题,全面优化。

有趣的结果:

1:13105

2: 13044

3: 4546

无初始化案例:906.

所以看起来很像 VS2010 使用 memset 来表示 1,但 fill 优化得更好。

#include "stdafx.h"
#include <Windows.h>
#include <algorithm>
#include <iostream>

int  fref()
{
    int a[1024];
    return a[512] - a[256];
}

int f1()
{
    int a[1024] = {};

    return a[512] - a[256];
}

int  f2()
{
    int a[1024];
    memset(a, 0, sizeof(a));
    return a[512] - a[256];
}



int f3()
{
    int a[1024];
    std::fill(a, a + 100, 0);
    return a[512] - a[256];
}

typedef int (*Function)();

LONGLONG time(Function function)
{
    const unsigned numLoops = 50000;
    LARGE_INTEGER start;
    QueryPerformanceCounter(&start);
    for(unsigned j = 1; j != numLoops; ++j)
    function();
    LARGE_INTEGER end;
    QueryPerformanceCounter(&end);
    return end.QuadPart-start.QuadPart;
}

Function tests[]= 
{
    &fref, &f1, &f2, &f3
};

const unsigned numTests = sizeof(tests)/sizeof(tests[0]);

LONGLONG results[numTests] = {};


int _tmain(int argc, _TCHAR* argv[])
{
    for(unsigned i = 0; i != numTests; ++i)
    {
        results[i] = time(tests[i]);
    }
    for(unsigned i = 0; i != numTests; ++i)
        std::cout << results[i] << std::endl;
    getchar();
    return 0;
}

【讨论】:

  • 为什么会这样?我原以为 memset 将是执行此操作的最低级别的方法(因此效率更高)。非常可疑...
  • 我的猜测是 memset 很慢,因为它是逐字节分配的,而 fill 是逐字填充的。除了 c 字符串之外,您不应该将 memset 用于其他任何内容的另一个原因。
  • memset 不知道它的参数是 32 位对齐的。它通常使用逐字写入,但需要特殊代码用于范围的开始和结束。对于更大的范围,这些成本会相对较低,但int[100] 并没有那么大。
【解决方案3】:

使用 Keith 的示例代码。 GCC下的区别如下:

GCC 4.7.3:g++ -Wall -Wextra -std=c++0x -O3 -c array-fill.cpp

#include <algorithm>
#include <cstring>

int  fref() {
    int a[1024];
    return a[512] - a[256]; }

int f1() {
    int a[1024] = {};
    return a[512] - a[256]; }

int  f2() {
    int a[1024];
    std::memset(a, 0, sizeof(a));
    return a[512] - a[256]; }

int f3() {
    int a[1024];
    std::fill(a, a + 100, 0);
    return a[512] - a[256]; }

拆解

objdump -d 数组填充.o | c++filt

00000000 <fref()>:
   0:   b8 00 10 00 00          mov    $0x1000,%eax
   5:   e8 00 00 00 00          call   a <fref()+0xa>
   a:   29 c4                   sub    %eax,%esp
   c:   8b 84 24 00 08 00 00    mov    0x800(%esp),%eax
  13:   2b 84 24 00 04 00 00    sub    0x400(%esp),%eax
  1a:   81 c4 00 10 00 00       add    $0x1000,%esp
  20:   c3                      ret
  21:   eb 0d                   jmp    30 <f1()>

00000030 <f1()>:
  30:   31 c0                   xor    %eax,%eax
  32:   c3                      ret
  33:   8d b6 00 00 00 00       lea    0x0(%esi),%esi
  39:   8d bc 27 00 00 00 00    lea    0x0(%edi,%eiz,1),%edi

00000040 <f2()>:
  40:   b8 00 10 00 00          mov    $0x1000,%eax
  45:   e8 00 00 00 00          call   4a <f2()+0xa>
  4a:   29 c4                   sub    %eax,%esp
  4c:   31 c0                   xor    %eax,%eax
  4e:   81 c4 00 10 00 00       add    $0x1000,%esp
  54:   c3                      ret
  55:   8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi
  59:   8d bc 27 00 00 00 00    lea    0x0(%edi,%eiz,1),%edi

00000060 <f3()>:
  60:   b8 00 10 00 00          mov    $0x1000,%eax
  65:   e8 00 00 00 00          call   6a <f3()+0xa>
  6a:   29 c4                   sub    %eax,%esp
  6c:   89 e0                   mov    %esp,%eax
  6e:   8d 94 24 90 01 00 00    lea    0x190(%esp),%edx
  75:   c7 00 00 00 00 00       movl   $0x0,(%eax)
  7b:   83 c0 04                add    $0x4,%eax
  7e:   39 d0                   cmp    %edx,%eax
  80:   75 f3                   jne    75 <f3()+0x15>
  82:   8b 84 24 00 08 00 00    mov    0x800(%esp),%eax
  89:   2b 84 24 00 04 00 00    sub    0x400(%esp),%eax
  90:   81 c4 00 10 00 00       add    $0x1000,%esp
  96:   c3                      ret

C 风格的初始化 (f1) 在这种情况下当然允许优化!

【讨论】:

    猜你喜欢
    • 2014-03-23
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 2021-06-15
    • 2011-08-03
    相关资源
    最近更新 更多