我在godbolt.org上用-O2编译了这段代码,编译器并没有使用一些memcpy来优化它,而是诚实地运行循环。
void foo(int* dst, int* src, int n)
{
for (int i = 0; i < n; ++i)
{
dst[i] = src[i];
}
}
但是如果我替换"=src[i]";其中"=0";不过他们使用memset。但是,当我用"=1〃;,它们循环运行。当要设置的值不为零时,它们为什么要避免memcpy和memset?我认为这是他们将执行的第一个优化。
src
和dest
指向的范围可能重叠,在这种情况下,memcpy
的行为将是未定义的。因此,将此函数优化为仅调用memcpy
是不合适的。
memmove
是合适的,但当src
和dest
范围重叠时,其行为与您的函数不同。考虑以下内容:
int arr[5] = {1, 2, 3, 4, 5};
foo(arr + 1, arr, 4);
您的函数将在调用后生成包含{1, 1, 1, 1, 1}
的arr
,而指定memmove
将生成包含{1, 1, 2, 3, 4}
的arr
。因此编译器也不能将foo
优化为对memmove
的调用。
C在C99中添加了restrict
关键字,以告诉编译器两个范围不会重叠,但C++没有采用该特定功能。
完成@MilesBudnek:的好答案
memset
以字节粒度工作,而您使用的int
通常大于1个字节(4个字节(。这就是为什么编译器不能容易地用memset替换赋值= 1
的原因。
还要注意的是,-O2
并不能为GCC启用矢量化,尽管它显然为Clang启用了。GCC需要-ftree-vectorize
(包括在-O3
中(来生成更快的SIMD指令(在许多平台上不如memcpy
/memmove
/memset
快(。