c++ - 为什么 Clang 不将这些连续和相邻的调用合并到 memcpy 或 memset?
问题描述
当使用 Clang 10.0.0 和当前的 Clang-trunk 编译它时,它似乎缺少一些非常明显的优化机会:
struct A {
int x[16] {0}; // Everything zero init by default
int y[16] {0};
int z[16] {0};
};
int foo(A* a, A* b); // dummy function in external translation unit
// to block any constant folding and unused variable
// elimination, etc.
int func() {
A a[1024],b[1024]; // clang calls memset for each of a and b
// could be optimized to single call
foo(a,b);
for( int i = 0 ; i < 1024 ; i++ ) {
a[i] = b[i]; // clang calls memcpy here 1024 times with
// consecutive addresses, could be optimized
// to single call.
}
return foo(a,b);
}
Clang 在 -O3 处为 x64 生成的输出如下: https ://godbolt.org/z/taKi4G
func(): # @func()
push r15
push r14
push rbx
sub rsp, 393216
lea r14, [rsp + 196608]
xor ebx, ebx
mov edx, 196608
mov rdi, r14
xor esi, esi
call memset
mov r15, rsp
mov edx, 196608
mov rdi, r15
xor esi, esi
call memset
mov rdi, r14
mov rsi, r15
call foo(A*, A*)
.LBB0_1: # =>This Inner Loop Header: Depth=1
lea rdi, [rsp + rbx]
add rdi, 196608
lea rsi, [rsp + rbx]
mov edx, 192
call memcpy
lea rdi, [rsp + rbx]
add rdi, 196800
lea rsi, [rsp + rbx]
add rsi, 192
mov edx, 192
call memcpy
add rbx, 384
cmp rbx, 196608
jne .LBB0_1
lea rdi, [rsp + 196608]
mov rsi, rsp
call foo(A*, A*)
add rsp, 393216
pop rbx
pop r14
pop r15
ret
我认为对 memset 的两个调用可以合并为一个 memset,对 memcpy 的 1024 个调用可以合并,留下一个 memset 和一个 memcpy。memset 的改进可能是微不足道的,但我很确定一次调用 memcpy 会明显更快。
我试图说服某人他们不应该在他们的代码中使用 mempcy 和 memset 来复制和归零 POD 结构的初始化数组,而应该只使用构造函数并让编译器处理它。当我实际查看 clang 生成的内容时,我的论点有些分崩离析。
进行这种合并是无效的还是clang只是错过了一个技巧?
编辑:我检查了 GCC 10,它似乎按照我的预期做了很大的 mempcy,但仍然没有合并 memset。
解决方案
推荐阅读
- ruby - 为外部 api Ruby on Rails 生成安全令牌
- aframe - 有没有一种简单的方法来获取 a-frame 声音的持续时间?
- video - 如何在不影响视频数据的情况下使用 ffmpeg 写入 mp4 元数据
- java - Java 的 CipherOutputStream 与 Apache 的 CryptoOutputStream 性能
- java - 不调用 Future.get 会导致任何问题吗?
- clips - CLIPS LHS 绑定变量
- c# - IDataRecord 读取器在调用 Linq.First() 时被关闭
- vb.net - 用 RichTextBox 中的 String 中的分号替换新行 - vb.net
- ruby-on-rails - 导轨。请求的资源上不存在“Access-Control-Allow-Origin”标头
- elasticsearch - Elasticsearch 5.5 - 如何从 _stats 返回 _all.total.store.size_in_bytes ?