memset比for循环更有效。所以,如果我有
char x[500];
memset(x,0,sizeof(x));
要么
char x[500];
for(int i = 0 ; i < 500 ; i ++) x[i] = 0;
哪一个更有效率,为什么?是否在硬件中有任何特殊指令来进行块级初始化。
当然,memset
会比那个循环快得多。请注意您一次如何处理一个字符,但这些函数经过优化,一次设置几个字节,即使使用MMX和SSE指令也是如此。
我认为这些优化的典型例子,通常不被注意,是GNU C库strlen
函数。有人会认为它至少具有O(n)性能,但它实际上有O(n / 4)或O(n / 8),具体取决于架构(是的,我知道,在大O()中将是相同的,但你实际上得到了八分之一的时间)。怎么样?棘手,但很好:strlen。
那么,为什么我们不看看VS 2010下生成的汇编代码,完全优化。
char x[500];
char y[500];
int i;
memset(x, 0, sizeof(x) );
003A1014 push 1F4h
003A1019 lea eax,[ebp-1F8h]
003A101F push 0
003A1021 push eax
003A1022 call memset (3A1844h)
你的循环......
char x[500];
char y[500];
int i;
for( i = 0; i < 500; ++i )
{
x[i] = 0;
00E81014 push 1F4h
00E81019 lea eax,[ebp-1F8h]
00E8101F push 0
00E81021 push eax
00E81022 call memset (0E81844h)
/* note that this is *replacing* the loop,
not being called once for each iteration. */
}
因此,在此编译器下,生成的代码完全相同。 memset
速度很快,编译器非常聪明,知道你做的事情和调用memset
一样,所以它适合你。
如果编译器实际上按原样离开了循环,那么它可能会慢一些,因为你可以一次设置多个字节大小的块(也就是说,你可以将循环展开到最小。你可以假设memset
将是至少和循环之类的天真实现一样快。在调试版本下尝试它,你会注意到循环没有被替换。
也就是说,这取决于编译器为您做什么。查看反汇编始终是确切知道发生了什么的好方法。
它真的取决于编译器和库。对于较旧的编译器或简单编译器,memset可能在库中实现,并且不会比自定义循环执行得更好。
对于几乎所有值得使用的编译器,memset是一个内部函数,编译器将为它生成优化的内联代码。
其他人建议进行剖析和比较,但我不打扰。只需使用memset。代码简单易懂。在您的基准测试告诉您这部分代码是性能热点之前,请不要担心。
答案是“这取决于”。 memset
可能更有效,或者它可能在内部使用for循环。我想不出memset
效率会降低的情况。在这种情况下,它可能会变成一个更有效的for循环:你的循环迭代500次,每次将数组的字节值设置为0。在64位机器上,你可以循环,一次设置8个字节(一个很长的长),这几乎要快8倍,并且最后只处理剩下的4个字节(500%8)。
编辑:
事实上,这就是memset
在glibc中的作用:
http://repo.or.cz/w/glibc.git/blob/HEAD:/string/memset.c
正如Michael指出的那样,在某些情况下(编译时已知数组长度),C编译器可以内联memset
,摆脱函数调用的开销。 Glibc还为大多数主要平台提供了memset
的汇编优化版本,例如amd64:
http://repo.or.cz/w/glibc.git/blob/HEAD:/sysdeps/x86_64/memset.S
好的编译器会识别for循环并用最佳内联序列或memset调用替换它。当缓冲区大小较小时,它们还将使用最佳内联序列替换memset。
实际上,使用优化编译器,生成的代码(以及性能)将是相同的。
同意上述内容。这取决于。但是,确保memset更快或等于for循环。如果您不确定您的环境或懒得测试,请采取安全路线并使用memset。
void fill_array(void* array, size_t size_of_item, size_t length, void* value) {
uint8_t* bytes = value;
uint8_t first_byte = bytes[0];
if (size_of_item == 1) {
memset(array, first_byte, length);
return;
}
// size_of_item > 1 here.
bool all_bytes_are_identical = true;
for (size_t byte_index = 1; byte_index < size_of_item; byte_index++) {
if (bytes[byte_index] != first_byte) {
all_bytes_are_identical = false;
break;
}
}
if (all_bytes_are_identical) {
memset(array, first_byte, size_of_item * length);
return;
}
for (size_t index = 0; index < length; index++) {
memcpy((uint8_t*)array + size_of_item * index, value, size_of_item);
}
}
memset
更有效率,它不应该关心非对称值(all_bytes_are_identical
是false
)。所以你将搜索如何包装它。
这是我的变种。它适用于小端和大端系统。