在《英特尔架构指令集扩展编程参考》的第2.5.3节“广播”中,我们了解到AVX512(和Knights Corner)具有的优势
一个位字段,用于对某些加载操作指令(即从内存加载数据并执行某些计算或数据移动操作的指令)进行编码的数据广播进行编码。
例如,使用Intel汇编语法,我们可以在存储的地址中广播标量,rax
然后乘以16个浮点数,zmm2
并将结果写成zmm1
这样
vmulps zmm1, zmm2, [rax] {1to16}
但是,没有内在函数可以做到这一点。因此,使用内在函数,编译器应该可以折叠
__m512 bb = _mm512_set1_ps(b);
__m512 ab = _mm512_mul_ps(a,bb);
一条指令
vmulps zmm1, zmm2, [rax] {1to16}
但我还没有观察到海湾合作委员会这样做。我发现了有关此的GCC错误报告。
我观察到与FCC和GCC相似。例如,GCC 4.9不会折叠_mm256_add_ps(_mm256_mul_ps(areg0,breg0)
为单个fma指令-Ofast
。但是,GCC 5.1现在确实将其折叠为单个fma。至少有一些内在函数可以通过FMA做到这一点,例如_mm256_fmadd_ps
。但是没有_mm512_mulbroad_ps(vector,scalar)
内在的。
GCC可能会在某个时候解决此问题,但是直到那时组装才是唯一的解决方案。
所以我的问题是如何在GCC中使用内联汇编实现此目的?
我想我为上面的示例为GCC内联汇编提出了正确的语法(但不确定)。
"vmulps (%%rax)%{1to16}, %%zmm1, %%zmm2\n\t"
我真的在寻找这样的功能
static inline __m512 mul_broad(__m512 a, float b) {
return a*b;
}
如果b
在内存中指向rax
它产生的位置
vmulps (%rax){1to16}, %zmm0, %zmm0
ret
如果b
在xmm1
其中产生
vbroadcastss %xmm1, %zmm1
vmulps %zmm1, %zmm0, %zmm0
ret
GCC已经vbroadcastss
使用内部函数处理-from-register情况,但是如果b
在内存中,则将其编译为vbroadcastss
from内存。
__m512 mul_broad(__m512 a, float b) {
__m512 bb = _mm512_set1_ps(b);
__m512 ab = _mm512_mul_ps(a,bb);
return ab;
}
正如Peter Cordes所述,GCC不允许您为不同的约束替代方案指定其他模板。因此,我的解决方案改为让汇编器根据所选的操作数选择正确的指令。
我没有支持ZMM寄存器的GCC版本,因此下面的示例使用XMM寄存器和一些不存在的指令来演示如何实现所需的功能。
typedef __attribute__((vector_size(16))) float v4sf;
v4sf
foo(v4sf a, float b) {
v4sf ret;
asm(".ifndef isxmm\n\t"
".altmacro\n\t"
".macro ifxmm operand, rnum\n\t"
".ifc \"\\operand\",\"%%xmm\\rnum\"\n\t"
".set isxmm, 1\n\t"
".endif\n\t"
".endm\n\t"
".endif\n\t"
".set isxmm, 0\n\t"
".set regnum, 0\n\t"
".rept 8\n\t"
"ifxmm <%2>, %%regnum\n\t"
".set regnum, regnum + 1\n\t"
".endr\n\t"
".if isxmm\n\t"
"alt-1 %1, %2, %0\n\t"
".else\n\t"
"alt-2 %1, %2, %0\n\t"
".endif\n\t"
: "=x,x" (ret)
: "x,x" (a), "x,m" (b));
return ret;
}
v4sf
bar(v4sf a, v4sf b) {
return foo(a, b[0]);
}
此示例应使用编译,gcc -m32 -msse -O3
并应生成两个类似于以下内容的汇编器错误消息:
t103.c: Assembler messages:
t103.c:24: Error: no such instruction: `alt-2 %xmm0,4(%esp),%xmm0'
t103.c:22: Error: no such instruction: `alt-1 %xmm0,%xmm1,%xmm0'
此处的基本思想是汇编程序检查第二个操作数(%2
)是否为XMM寄存器还是其他(可能是内存位置)。由于GNU汇编器对字符串的操作方式不支持太多,因此第二个操作数在.rept
循环中一次与每个可能的XMM寄存器进行比较。该isxmm
宏用于粘贴%xmm
和一个寄存器号在一起。
对于您的特定问题,您可能需要像这样重写它:
__m512
mul_broad(__m512 a, float b) {
__m512 ret;
__m512 dummy;
asm(".ifndef isxmm\n\t"
".altmacro\n\t"
".macro ifxmm operand, rnum\n\t"
".ifc \"\\operand\",\"%%zmm\\rnum\"\n\t"
".set isxmm, 1\n\t"
".endif\n\t"
".endm\n\t"
".endif\n\t"
".set isxmm, 0\n\t"
".set regnum, 0\n\t"
".rept 32\n\t"
"ifxmm <%[b]>, %%regnum\n\t"
".set regnum, regnum + 1\n\t"
".endr\n\t"
".if isxmm\n\t"
"vbroadcastss %x[b], %[b]\n\t"
"vmulps %[a], %[b], %[ret]\n\t"
".else\n\t"
"vmulps %[b] %{1to16%}, %[a], %[ret]\n\t"
"# dummy = %[dummy]\n\t"
".endif\n\t"
: [ret] "=x,x" (ret), [dummy] "=xm,x" (dummy)
: [a] "x,xm" (a), [b] "m,[dummy]" (b));
return ret;
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句