为什么AsDouble1比 更简单AsDouble0?
// AsDouble0(unsigned long): # @AsDouble0(unsigned long)
// movq xmm1, rdi
// punpckldq xmm1, xmmword ptr [rip + .LCPI0_0] # xmm1 = xmm1[0],mem[0],xmm1[1],mem[1]
// subpd xmm1, xmmword ptr [rip + .LCPI0_1]
// movapd xmm0, xmm1
// unpckhpd xmm0, xmm1 # xmm0 = xmm0[1],xmm1[1]
// addsd xmm0, xmm1
// addsd xmm0, xmm0
// ret
double AsDouble0(uint64_t x) { return x * 2.0; }
// AsDouble1(unsigned long): # @AsDouble1(unsigned long)
// shr rdi
// cvtsi2sd xmm0, rdi …Run Code Online (Sandbox Code Playgroud) 我尝试过加快玩具 GEMM 的实施速度。我处理 32x32 双精度块,为此我需要优化的 MM 内核。我可以访问 AVX2 和 FMA。
我在下面定义了两个代码(在 ASM 中,我为格式的粗糙性表示歉意),一个使用 AVX2 功能,另一个使用 FMA。
在不进行微观基准测试的情况下,我想尝试(理论上)理解为什么 AVX2 实现比 FMA 版本快 1.11 倍。以及可能如何改进这两个版本。
下面的代码适用于 3000x3000 双打 MM,并且内核是使用经典的朴素 MM 和可互换的最深循环来实现的。我使用 Ryzen 3700x/Zen 2 作为开发 CPU。
我没有尝试过积极展开,担心 CPU 可能会耗尽物理寄存器。
AVX2 32x32 MM 内核:
Block 82:
imul r12, r15, 0xbb8
mov rax, r11
mov r13d, 0x0
vmovupd ymm0, ymmword ptr [rdi+r12*8]
vmovupd ymm1, ymmword ptr [rdi+r12*8+0x20]
vmovupd ymm2, ymmword ptr [rdi+r12*8+0x40]
vmovupd ymm3, ymmword ptr [rdi+r12*8+0x60]
vmovupd ymm4, ymmword ptr [rdi+r12*8+0x80]
vmovupd ymm5, …Run Code Online (Sandbox Code Playgroud) 我正在研究高度“可向量化”的代码,并注意到关于 C++ __restrict 关键字/扩展 ~,即使在简单的情况下,与 GCC 相比,Clang 的行为也是不同且不切实际的。
对于编译器生成的代码,速度减慢约 15 倍(在我的具体情况下,不是下面的示例)。
这是代码(也可在https://godbolt.org/z/sdGd43x75获取):
struct Param {
int *x;
};
int foo(int *a, int *b) {
*a = 5;
*b = 6;
// No significant optimization here, as expected (for clang/gcc)
return *a + *b;
}
int foo(Param a, Param b) {
*a.x = 5;
*b.x = 6;
// No significant optimization here, as expected (for clang/gcc)
return *a.x + *b.x;
}
/////////////////////
struct ParamR {
// "Restricted pointers assert …Run Code Online (Sandbox Code Playgroud) 假设我有这个预处理器检查:
#if(-6 & 5)
#error "No 2's complement signed int"
#endif
Run Code Online (Sandbox Code Playgroud)
如果我从二进制补码机交叉编译到二进制补码机,会发生什么情况。编译器会使用目标机器的算术,还是编译机器的算术?
谢谢