4

我试图了解使用 SSE 指令进行矢量化是如何工作的。

这是实现矢量化的代码片段:

#include <stdlib.h>
#include <stdio.h>

#define SIZE 10000

void test1(double * restrict a, double * restrict b)
{
  int i;

  double *x = __builtin_assume_aligned(a, 16);
  double *y = __builtin_assume_aligned(b, 16);

  for (i = 0; i < SIZE; i++)
  {
    x[i] += y[i];
  }
}

和我的编译命令:

gcc -std=c99 -c example1.c -O3 -S -o example1.s

这里是汇编代码的输出:

 .file "example1.c"
  .text
  .p2align 4,,15
  .globl  test1
  .type test1, @function
test1:
.LFB7:
  .cfi_startproc
  xorl  %eax, %eax
  .p2align 4,,10
  .p2align 3
.L3:
  movapd  (%rdi,%rax), %xmm0
  addpd (%rsi,%rax), %xmm0
  movapd  %xmm0, (%rdi,%rax)
  addq  $16, %rax
  cmpq  $80000, %rax
  jne .L3
  rep ret
  .cfi_endproc
.LFE7:
  .size test1, .-test1
  .ident  "GCC: (Debian 4.8.2-16) 4.8.2"
  .section  .note.GNU-stack,"",@progbits

多年前我练习过汇编程序,我想知道寄存器 %rdi、%rax 和 %rsi 上面代表什么。

我知道 %xmm0 是 SIMD 寄存器,我们可以在其中存储 2 个双精度数(16 个字节)。

但我不明白如何同时添加:

我认为一切都发生在这里:

      movapd  (%rdi,%rax), %xmm0
      addpd (%rsi,%rax), %xmm0
      movapd  %xmm0, (%rdi,%rax)
      addq  $16, %rax
      cmpq  $80000, %rax
      jne .L3
      rep ret

%rax 是否代表“x”数组?

%rsi 在 C 代码片段中代表什么?

最终结果(例如 a[0]=a[0]+b[0] 是否存储到 %rdi 中?

谢谢你的帮助

4

2 回答 2

5

您需要了解的第一件事是 Unix 系统上 64 位代码的调用约定。请参阅Wikipedia 的 x86-64_calling_conventions,有关更多详细信息,请阅读 Agner Fog 的调用约定手册

整数参数按以下顺序传递:rdi、rsi、rdx、rcx、r8、r9。因此,您可以通过寄存器传递六个整数值(但在 Windows 上只有四个)。这意味着在您的情况下:

rdi = &x[0],
rsi = &y[0].

寄存器rax从零开始,2*sizeof(double)=16每次迭代递增字节。然后将其与sizeof(double)*10000=80000每次迭代进行比较以测试循环是否完成。

这里的使用cmp其实是GCC编译器的一种低效。现代英特尔处理器可以将cmpandjne指令融合到一条指令中,也可以将addand融合jne到一条指令中,但它们不能add将, cmp, and融合jne到一条指令中。但是可以删除cmp指令

GCC应该做的是设置

rdi = &x[0] + 80000;
rsi = &y[0] + 80000;
rax = -80000

然后循环可以像这样完成

movapd  (%rdi,%rax), %xmm0       ; temp = x[i]
addpd (%rsi,%rax), %xmm0         ; temp += y[i]
movapd  %xmm0, (%rdi,%rax)       ; x[i] = temp
addq  $16, %rax                  ; i += 2
jnz .L3                          ; then loop

现在循环从-80000up 开始计数,0不需要cmp指令,addandjnz将融合为一个微操作。

于 2014-10-15T07:57:38.357 回答
3
  movapd  (%rdi,%rax), %xmm0       ; temp = x[i]
  addpd (%rsi,%rax), %xmm0         ; temp += y[i]
  movapd  %xmm0, (%rdi,%rax)       ; x[i] = temp
  addq  $16, %rax                  ; i += 2
  cmpq  $80000, %rax               ; if (i < SIZE)
  jne .L3                          ; then loop

rax 寄存器表示i变量,但存储为字节索引,rdi 为 &x,rsi 为 &y。每次通过循环都会添加两个双精度数,因此 rax 的增量为 2 * sizeof(double) 或 16 个字节。

于 2014-10-14T22:04:52.553 回答