这适用于memset()
和memcpy()
:
- 更少的代码:正如您已经提到的,它更短 - 更少的代码行。
- 更具可读性:更短通常也使其更具可读性。(
memset()
比那个循环更具可读性)
- 它可以更快:它有时可以允许更积极的编译器优化。(所以它可能会更快)
- 未对齐:在某些情况下,当您在不支持未对齐访问的处理器上处理未对齐的数据时,
memset()
可能memcpy()
是唯一干净的解决方案。
为了扩展第三点,memset()
可以通过使用 SIMD 等的编译器进行大量优化。如果您改为编写循环,则编译器首先需要“弄清楚”它的作用,然后才能尝试对其进行优化。
这里的基本思想是memset()
,在某种意义上,类似的库函数“告诉”编译器你的意图。
正如@Oli 在评论中提到的那样,有一些缺点。我将在这里扩展它们:
- 您需要确保它
memset()
确实可以满足您的需求。该标准并没有说各种数据类型的零在内存中一定是零。
- 对于非零数据,
memset()
仅限于 1 字节内容。memset()
因此,如果要将 s 数组设置int
为零以外的值(或0x01010101
或其他值...),则不能使用。
- 虽然很少见,但在某些极端情况下,实际上可以使用自己的循环在性能上击败编译器。*
*我将根据我的经验举一个例子:
尽管memset()
和memcpy()
通常是编译器内部函数,由编译器进行特殊处理,但它们仍然是通用函数。他们对包括数据对齐在内的数据类型只字未提。
因此在少数(尽管很少见)情况下,编译器无法确定内存区域的对齐方式,因此必须生成额外的代码来处理未对齐情况。然而,如果你是程序员,100% 确定对齐,使用循环实际上可能更快。
一个常见的例子是使用 SSE/AVX 内部函数时。(例如复制 s 的 16/32 字节对齐数组float
)如果编译器无法确定 16/32 字节对齐,则需要使用未对齐的加载/存储和/或处理代码。如果您只是使用 SSE/AVX 对齐的加载/存储内在函数编写一个循环,您可能会做得更好。
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));
}