умножение с использованием SSE (x * x * x) + (y * y * y) - PullRequest
0 голосов
/ 02 декабря 2011

Я пытаюсь оптимизировать эту функцию с помощью SIMD, но я не знаю, с чего начать.

long sum(int x,int y)
{
    return x*x*x+y*y*y;
}

Разобранная функция выглядит следующим образом:

  4007a0:   48 89 f2                mov    %rsi,%rdx
  4007a3:   48 89 f8                mov    %rdi,%rax
  4007a6:   48 0f af d6             imul   %rsi,%rdx
  4007aa:   48 0f af c7             imul   %rdi,%rax
  4007ae:   48 0f af d6             imul   %rsi,%rdx
  4007b2:   48 0f af c7             imul   %rdi,%rax
  4007b6:   48 8d 04 02             lea    (%rdx,%rax,1),%rax
  4007ba:   c3                      retq   
  4007bb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

Код вызова выглядит следующим образом:

 do {
for (i = 0; i < maxi; i++) {
  j = nextj[i];
  long sum = cubeSum(i,j);
  while (sum <= p) {
    long x = sum & (psize - 1);
    int flag = table[x];
    if (flag <= guard) {
      table[x] = guard+1;
    } else if (flag == guard+1) {
      table[x] = guard+2;
      count++;
    }
    j++;
    sum = cubeSum(i,j);
  }
  nextj[i] = j;
}
p += psize;
guard += 3;
} while (p <= n);

Ответы [ 2 ]

6 голосов
/ 02 декабря 2011
  • Заполните один регистр SSE (x | y | 0 | 0) (поскольку каждый регистр SSE содержит 4 32-битных элемента).Давайте назовем его r1
  • , затем сделаем копию этого регистра в другой регистр r2
  • Do r2 * r1, сохраняя результат, скажем, в r2.
  • Do r2 * r1снова сохраняя результат в r2
  • Теперь в r2 у вас есть (x * x * x | y * y * y | 0 | 0)
  • Распакуйте два нижних элемента r2 в отдельные регистры, добавьте их (SSE3 имеет горизонтальные инструкции добавления, но только для чисел с плавающей запятой и двойных чисел).

В конце концов, я бы на самом деле удивился, если бы это оказалось быстрее, чем простой кодкомпилятор уже сгенерирован для вас.SIMD более полезен, если у вас есть массивы данных, с которыми вы хотите работать ..

1 голос
/ 02 декабря 2011

Этот конкретный случай не подходит для SIMD (SSE или иным образом).SIMD действительно хорошо работает только тогда, когда у вас есть смежные массивы, к которым вы можете обращаться последовательно и обрабатывать неоднородно.

Однако вы можете по крайней мере избавиться от некоторых избыточных операций в скалярном коде, например, многократного вычисления i * i * i приi является инвариантом:

do {
    for (i = 0; i < maxi; i++) {
        int i3 = i * i * i;
        int j = nextj[i];
        int j3 = j * j * j;
        long sum = i3 + j3;
        while (sum <= p) {
            long x = sum & (psize - 1);
            int flag = table[x];
            if (flag <= guard) {
              table[x] = guard+1;
            } else if (flag == guard+1) {
              table[x] = guard+2;
              count++;
            }
            j++;
            j3 = j * j * j;
            sum = i3 + j3;
        }
        nextj[i] = j;
    }
    p += psize;
    guard += 3;
} while (p <= n);
...