为了避免违反严格的别名规则,我在代码中的几个地方引入了memcpy
,希望它是一个禁忌。以下示例在gcc和clang上生成对memcpy
(或等效文件)的调用。具体来说,fool<40>
总是在foo
对gcc而不是clang上起作用,而fool<2>
对ccc而不是gcc起作用。何时/如何对其进行优化?
uint64_t bar(const uint16_t *buf) {
uint64_t num[2];
memcpy(&num, buf, 16);
return num[0] + num[1];
}
uint64_t foo(const uint16_t *buf) {
uint64_t num[3];
memcpy(&num, buf, sizeof(num));
return num[0] + num[1];
}
template <int SZ>
uint64_t fool(const uint16_t *buf) {
uint64_t num[SZ];
memcpy(&num, buf, sizeof(num));
uint64_t ret = 0;
for (int i = 0; i < SZ; ++i)
ret += num[i];
return ret;
}
template uint64_t fool<2>(const uint16_t*);
template uint64_t fool<40>(const uint16_t*);
答案 0 :(得分:2)
我真的不能告诉您为什么各个编译器无法按照您希望它们在特定情况下进行优化的方式来优化代码。我猜每个编译器要么无法跟踪目标阵列和源内存之间的memcpy建立的关系(如我们所见,它们似乎至少在某些情况下确实认识到这种关系),或者它们只是有些启发式的判断他们选择不使用它。
无论如何,由于编译器似乎不像我们希望的那样依赖它们跟踪整个数组,因此我们可以尝试通过仅对每个元素执行memcpy使其对编译器更加明显元素基础。 seems to produce the desired result on both compilers。请注意,我必须手动展开bar
和foo
中的初始化,因为clang会再次进行复制。
除此之外,请注意,在C ++中,您应该使用std::memcpy
,std::uint64_t
等,因为标准标头are not guaranteed还将这些名称引入了全局名称空间(尽管我我不知道任何不执行该操作的实现。