gpt4 book ai didi

gcc - GCC 内置向量化类型和 C 数组之间有什么区别?

转载 作者:行者123 更新时间:2023-12-03 17:52:58 24 4
gpt4 key购买 nike

我有三个功能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);
}

当我编译这段代码时,我观察到以下行为:
  • 打电话时printv()a()所有 4 个浮点数都被 %xmm0 传递.不会发生对内存的写入。
  • 打电话时printv()b() 2 个浮点数正在通过 %xmm0和另外两个 float %xmm1 .为此,将 4 个浮点数加载 (.LC0) 到 %xmm2从那里到内存。之后,从内存中的同一个地方读取2个浮点数到%xmm0和其他 2 个浮点数加载 (.LC1) 到 %xmm1 .
  • 我有点迷茫 c()实际上确实如此。

  • 为什么是 a() , b()c()不同的?

    这是 a() 的汇编输出:
            vmovaps .LC0(%rip), %xmm0
    call _Z6printvU8__vectorf

    b() 的汇编输出:
            vmovaps .LC0(%rip), %xmm2
    vmovaps %xmm2, (%rsp)
    vmovq .LC1(%rip), %xmm1
    vmovq (%rsp), %xmm0
    call _Z6printv5Union

    以及 c() 的汇编输出:
             andq    $-32, %rsp
    subq $32, %rsp
    vmovaps .LC0(%rip), %xmm0
    vmovaps %xmm0, (%rsp)
    vmovq .LC2(%rip), %xmm0
    vmovq 8(%rsp), %xmm1
    call _Z6printv6Struct

    数据:
            .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

    四方 4647714816524288000似乎无非就是花车 3.04.0在相邻的长词中。

    最佳答案

    好问题,我不得不挖一点,因为我从来没有用过 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

    在您的第一种情况下 a()您将 SIMD 向量化与
    typedef float Builtin __attribute__ ((vector_size (16)));

    这允许您将整个向量一次移入 XMM0 寄存器。现在在你的第二种情况 b()你使用工会。但是因为您没有将 .LC0 加载到与 Union m.b = { 1.0, 2.0, 3.0, 4.0 }; 的联合中数据不被识别为矢量化。这会导致以下行为:

    来自 .LC0 的数据加载到 XMM2 中:
     vmovaps .LC0(%rip), %xmm2

    但是因为您的数据可以解释为结构 作为矢量化,数据必须分成两个 64 位块,它们仍然必须在 XMM(0-7) 寄存器中,因为它可以被视为矢量化,但它必须最大 64 位长才能传输到一个寄存器(它只有 64 位宽,如果将 128 位传输到它会溢出;数据丢失),因为数据也可以被视为一个结构。这是在下面完成的。

    XMM2 中的矢量化加载到内存中
        vmovaps %xmm2, (%rsp)

    现在是矢量化的高 64 位(位 64-127),即浮点数 3.04.0移动(vmovq 移动四字,即 64 位)到 XMM1
        vmovq   .LC1(%rip), %xmm1

    最后是矢量化的低 64 位(位 0-63),即浮点数 1.02.0从内存移动到 XMM0
        vmovq   (%rsp), %xmm0

    现在您在单独的 XMM(0-7) 寄存器中拥有 128 位向量的上部和下部。

    现在以防万一 c()我也不太确定,但就是这样。首先 %rsp 与 32 位地址对齐,然后减去 32 字节以将数据存储在堆栈上(这将再次与 32 位地址对齐)这是通过
         andq    $-32, %rsp
    subq $32, %rsp

    现在这次矢量化被加载到 XMM0 中,然后用
         vmovaps .LC0(%rip), %xmm0
    vmovaps %xmm0, (%rsp)

    最后向量化的高 64 位存储在 XMM0 中,低 64 位存储在 XMM1 寄存器中
         vmovq   .LC2(%rip), %xmm0
    vmovq 8(%rsp), %xmm1

    在所有三种情况下,矢量化的处理方式不同。希望这可以帮助。

    关于gcc - GCC 内置向量化类型和 C 数组之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16618998/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com