Tom*_*uwé 5 assembly gcc sse vectorization
我有三个功能a(),b()和c()那些应该做同样的事情:
typedef float Builtin __attribute__ ((vector_size (16)));
typedef struct {
float values[4];
} Struct;
typedef union {
Builtin b;
Struct s;
} Union;
extern void printv(Builtin);
extern void printv(Union);
extern void printv(Struct);
int a() {
Builtin m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
int b() {
Union m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
int c() {
Struct m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
Run Code Online (Sandbox Code Playgroud)
当我编译此代码时,我观察到以下行为:
printv()在a()所有4个浮标被传递%xmm0.不会写入内存.printv()在b()2漂浮在正在通过%xmm0和其他两个浮动的%xmm1.为了实现这一点,将4个浮点数(.LC0)加载%xmm2到存储器并从那里加载到存储器.之后,从内存中的相同位置读取%xmm02个浮点数,并加载另外2个浮点数(.LC1)%xmm1.c().为什么a(),b()和c()不同?
这是()的汇编输出:
vmovaps .LC0(%rip), %xmm0
call _Z6printvU8__vectorf
Run Code Online (Sandbox Code Playgroud)
b()的汇编输出:
vmovaps .LC0(%rip), %xmm2
vmovaps %xmm2, (%rsp)
vmovq .LC1(%rip), %xmm1
vmovq (%rsp), %xmm0
call _Z6printv5Union
Run Code Online (Sandbox Code Playgroud)
并且c()的汇编输出:
andq $-32, %rsp
subq $32, %rsp
vmovaps .LC0(%rip), %xmm0
vmovaps %xmm0, (%rsp)
vmovq .LC2(%rip), %xmm0
vmovq 8(%rsp), %xmm1
call _Z6printv6Struct
Run Code Online (Sandbox Code Playgroud)
数据:
.section .rodata.cst16,"aM",@progbits,16
.align 16
.LC0:
.long 1065353216
.long 1073741824
.long 1077936128
.long 1082130432
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC1:
.quad 4647714816524288000
.align 8
.LC2:
.quad 4611686019492741120
Run Code Online (Sandbox Code Playgroud)
四边形4647714816524288000似乎只不过是花车3.0和4.0相邻的长词.
小智 1
好问题,我不得不深入挖掘一下,因为我自己从未使用过SSE(在本例中为 SSE2)。本质上,向量指令用于对存储在一个寄存器(即 XMM(0-7) 寄存器)中的多个值进行操作。在 C 中,数据类型 float 使用IEEE 754,因此其长度为 32 位。使用四个浮点数将产生一个长度为 128 位的向量,这正是 XMM(0-7) 寄存器的长度。现在SSE提供的寄存器如下所示:
SSE (avx-128): |----------------|name: XMM0; size: 128bit
SSE (avx-256): |----------------|----------------|name: YMM0; size: 256bit
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,a()您使用 SIMD 矢量化
typedef float Builtin __attribute__ ((vector_size (16)));
Run Code Online (Sandbox Code Playgroud)
它允许您一次性将整个向量移入 XMM0 寄存器。现在,在第二种情况下,b()您使用联合。但因为您没有将 .LC0 加载到与Union m.b = { 1.0, 2.0, 3.0, 4.0 };数据的联合中,所以不会将其识别为矢量化。这会导致以下行为:
来自 .LC0 的数据通过以下方式加载到 XMM2 中:
vmovaps .LC0(%rip), %xmm2
Run Code Online (Sandbox Code Playgroud)
但是因为您的数据可以被解释为结构 或矢量化,所以数据必须分成两个 64 位块,它们仍然必须位于 XMM(0-7) 寄存器中,因为它可以被视为矢量化,但它最大长度必须为 64 位,才能传输到寄存器(寄存器只有 64 位宽,如果传输 128 位,则会溢出;数据丢失),因为数据也可以被视为结构。这是在下面完成的。
XMM2 中的矢量化加载到内存中
vmovaps %xmm2, (%rsp)
Run Code Online (Sandbox Code Playgroud)
现在向量化的高 64 位(位 64-127),即浮点3.0和4.0被移动(vmovq 移动四字,即 64 位)到 XMM1
vmovq .LC1(%rip), %xmm1
Run Code Online (Sandbox Code Playgroud)
最后矢量化的低 64 位(位 0-63)即浮点1.0并2.0从内存移动到 XMM0
vmovq (%rsp), %xmm0
Run Code Online (Sandbox Code Playgroud)
现在,128 位向量的上部和下部位于单独的 XMM(0-7) 寄存器中。
现在以防万一c()我也不太确定,但就这样了。首先 %rsp 与 32 位地址对齐,然后减去 32 字节以将数据存储在堆栈上(这将再次与 32 位地址对齐),这是通过
andq $-32, %rsp
subq $32, %rsp
Run Code Online (Sandbox Code Playgroud)
现在,这次矢量化被加载到 XMM0 中,然后放入堆栈中
vmovaps .LC0(%rip), %xmm0
vmovaps %xmm0, (%rsp)
Run Code Online (Sandbox Code Playgroud)
最后向量化的高 64 位存储在 XMM0 中,低 64 位存储在 XMM1 寄存器中
vmovq .LC2(%rip), %xmm0
vmovq 8(%rsp), %xmm1
Run Code Online (Sandbox Code Playgroud)
在这三种情况下,矢量化的处理方式有所不同。希望这可以帮助。
| 归档时间: |
|
| 查看次数: |
714 次 |
| 最近记录: |