这适用于两者memset()
and memcpy()
:
-
更少的代码:正如您已经提到的,它更短,代码行更少。
-
更具可读性:较短通常也使其更具可读性。 (
memset()
比那个循环更具可读性)
-
它可以更快:有时它可以允许更积极的编译器优化。 (所以可能会更快)
-
错位:在某些情况下,当您在不支持未对齐访问的处理器上处理未对齐的数据时,
memset()
and memcpy()
可能是唯一干净的解决方案。
对第3点进行扩展,memset()
编译器可以使用 SIMD 等进行大量优化。如果您编写一个循环,则编译器首先需要“弄清楚”它的作用,然后才能尝试优化它。
这里的基本思想是memset()
和类似的库函数,在某种意义上,“告诉”编译器你的意图。
正如 @Oli 在评论中提到的,有一些缺点。我将在这里扩展它们:
- 你需要确保
memset()
实际上做你想做的事。该标准并未规定各种数据类型的零在内存中必然为零。
- 对于非零数据,
memset()
仅限于 1 字节内容。所以你不能使用memset()
如果你想设置一个数组int
s 为零以外的值(或0x01010101
或者其他的东西...)。
- 尽管很少见,但在某些极端情况下,实际上可以使用自己的循环在性能上击败编译器。*
*我将根据我的经验举一个例子:
虽然memset()
and memcpy()
通常是经过编译器特殊处理的编译器内在函数,它们仍然是generic功能。他们没有提及数据类型,包括数据的对齐方式。
因此,在少数(尽管罕见)情况下,编译器无法确定内存区域的对齐方式,因此必须生成额外的代码来处理未对齐情况。然而,如果您是程序员,100% 确定对齐,那么使用循环实际上可能会更快。
一个常见的例子是使用 SSE/AVX 内在函数。 (例如复制一个 16/32 字节对齐的数组float
s) 如果编译器无法确定 16/32 字节对齐,则需要使用未对齐的加载/存储和/或处理代码。如果您只是使用 SSE/AVX 对齐的加载/存储内在函数编写一个循环,您可以probably做得更好。
float *ptrA = ... // some unknown source, guaranteed to be 32-byte aligned
float *ptrB = ... // some unknown source, guaranteed to be 32-byte aligned
int length = ... // some unknown source, guaranteed to be multiple of 8
// memcopy() - Compiler can't read comments. It doesn't know the data is 32-byte
// aligned. So it may generate unnecessary misalignment handling code.
memcpy(ptrA, ptrB, length * sizeof(float));
// This loop could potentially be faster because it "uses" the fact that
// the pointers are aligned. The compiler can also further optimize this.
for (int c = 0; c < length; c += 8){
_mm256_store_ps(ptrA + c, _mm256_load_ps(ptrB + c));
}