我的程序添加了浮点数组,并且在通过 MSVC 和 G++ 进行最大优化编译时展开了 4 倍。我不明白为什么两个编译器都选择展开 4x,所以我做了一些测试,发现只是偶尔在运行时手动展开 1-vs-2 或 1-vs-4 迭代的 t 测试给出 p 值 ~0.03, 2-vs-4 很少 < 0.05,2-vs-8+ 总是 > 0.05。
如果我将编译器设置为使用 128 位向量或 256 位向量,它总是展开 4x,这是 64 字节缓存行的倍数(有意义还是巧合?)。
我考虑缓存行的原因是因为我没想到展开会对顺序读取和写入千兆字节浮点数的内存绑定程序产生任何影响。在这种情况下展开是否有好处?也有可能没有显着差异,而且我的样本量不够大。
我发现这个博客说,对于中等大小的数组,手动展开数组副本速度更快,而对于较长的数组,流式传输速度最快。他们的 AvxAsyncPFCopier 和 AvxAsyncPFUnrollCopier 函数似乎受益于使用整个缓存行以及手动展开。博客中的基准测试及其来源在这里。
#include <iostream>
#include <immintrin.h>
int main() {
// example of manually unrolling float arrays
size_t bytes = sizeof(__m256) * 10;
size_t alignment = sizeof(__m256);
// 10 x 32-byte vectors
__m256* a = (__m256*) _mm_malloc(bytes, alignment);
__m256* b = …Run Code Online (Sandbox Code Playgroud) 只有1种情况__builtin_clz给出错误的答案。我很好奇是什么导致了这种行为。
当我使用文字值0时,我总是得到32的期望值。但是0作为变量将产生31。为什么存储值0的方法很重要?
我上过架构课程,但不了解差异化的程序集。看起来当给定字面值0时,即使不进行优化,该汇编总会以某种方式始终具有32个硬编码的正确答案。使用-march = native时,用于计算前导零的方法也不同。
这篇文章关于模拟__builtin_clz与_BitScanReverse和行bsrl %eax, %eax似乎意味着位扫描反向不起作用0。
+-------------------+-------------+--------------+
| Compile | literal.cpp | variable.cpp |
+-------------------+-------------+--------------+
| g++ | 32 | 31 |
| g++ -O | 32 | 32 |
| g++ -march=native | 32 | 32 |
+-------------------+-------------+--------------+
Run Code Online (Sandbox Code Playgroud)
#include <iostream>
int main(){
int i = 0;
std::cout << __builtin_clz(0) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
#include <iostream>
int main(){
int i = 0;
std::cout << __builtin_clz(i) << std::endl;
} …Run Code Online (Sandbox Code Playgroud)