清除一个小整数数组:memset与for循环

Cla*_*diu 56 c performance benchmarking

有两种方法可以将整数/浮点数组清零:

memset(array, 0, sizeof(int)*arraysize);
Run Code Online (Sandbox Code Playgroud)

要么:

for (int i=0; i <arraysize; ++i)
    array[i]=0;
Run Code Online (Sandbox Code Playgroud)

显然,memset对于大型更快arraysize.但是,在什么时候memset的开销实际上大于for循环的开销?例如,对于大小为5的数组 - 哪个最好?第一个,第二个,甚至可能是未滚动的版本:

array[0] = 0;
array[1] = 0;
array[2] = 0;
array[3] = 0;
array[4] = 0;
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 45

很可能,memset()将由编译器内联(大多数编译器将其视为'内在',这基本上意味着它是内联的,除非可能是最低优化或除非明确禁用).

例如,以下是GCC 4.3的一些发行说明:

重写了块移动(memcpy)和块集(memset)的代码生成.GCC现在可以根据要复制的块的大小和优化的CPU来选择最佳算法(循环,展开循环,带有rep前缀的指令或库调用).-minline-stringops-dynamically添加了一个新选项 .使用此选项,将扩展未知大小的字符串操作,以便通过内联代码复制小块,而对于大块,则使用库调用.-minline-all-stringops与库实现能够使用缓存层次结构提示相比,这会产生更快的代码 .选择特定算法的启发式算法可以通过覆盖 -mstringop-strategy.最新memset的不同于0的值也 被内联.

编译器可能会使用您提供的替代示例执行类似操作,但我敢打赌它不太可能.

而且grep一眼就能看出启动的目的是什么,而且更直接明显(不是循环特别难以理解).

  • 这是经常听到的"编译器在优化时优于您"的一个很好的例子.很少有应用程序员会在一次调用中花费这么多的注意力(如果他们这样做,他们的实际应用程序可能会受到影响).:) (10认同)
  • 放松,如果您认为"编译器比您更好"是您应该遵循的,请查看 - http://www.liranuna.com/sse-intrinsics-optimizations-in-popular-compilers/ (2认同)

小智 21

正如迈克尔已经指出的那样,gcc和我猜大多数其他编译器已经非常好地优化了它.例如gcc就是这样

char arr[5];
memset(arr, 0, sizeof arr);
Run Code Online (Sandbox Code Playgroud)

movl  $0x0, <arr+0x0>
movb  $0x0, <arr+0x4>
Run Code Online (Sandbox Code Playgroud)

它没有比那更好......


Rod*_*ddy 8

没有测量就无法回答这个问题.它完全取决于编译器,cpu和运行时库实现.

memset()可能是一个"代码味道",因为它可能容易出现缓冲区溢出,参数反转,并且具有仅清除"逐字节"的不幸能力.然而,可以肯定的是,除了极端情况外,它将"最快".

我倾向于使用宏来包装它以避免一些问题:

#define CLEAR(s) memset(&(s), 0, sizeof(s))
Run Code Online (Sandbox Code Playgroud)

这避免了尺寸计算,并消除了交换长度和值的参数问题.

简而言之,使用memset()"引擎盖下".写下你想要的东西,让编译器担心优化.大多数人都非常擅长.

  • 你的宏会很难用这样的代码来发现问题:`char*foo = malloc(80); CLEAR(FOO);` (8认同)