Вопрос

У меня есть код в петле

for(int i = 0; i < n; i++)
{
  u[i] = c * u[i] + s * b[i];
}

Итак, U и B - векторы одинаковой длины, а C и S - скалярные. Этот код хороший кандидат на векторизацию для использования с SSE, чтобы получить ускорение?

ОБНОВИТЬ

Я изучал векторизацию (оказывается, это не так сложно, если вы используете внутренние) и реализовали мою петлю в SSE. Однако при настройке флага SSE2 в компиляторе VC ++ я получаю одинаковую производительность, что и у моего собственного кода SSE. Компилятор Intel с другой стороны, был намного быстрее, чем мой код SSE или компилятора VC ++.

Вот код, который я написал для справки

double *u = (double*) _aligned_malloc(n * sizeof(double), 16);
for(int i = 0; i < n; i++)
{
   u[i] = 0;
}

int j = 0;
__m128d *uSSE = (__m128d*) u;
__m128d cStore = _mm_set1_pd(c);
__m128d sStore = _mm_set1_pd(s);
for (j = 0; j <= i - 2; j+=2)
{
  __m128d uStore = _mm_set_pd(u[j+1], u[j]);

  __m128d cu = _mm_mul_pd(cStore, uStore);
  __m128d so = _mm_mul_pd(sStore, omegaStore);

  uSSE[j/2] = _mm_add_pd(cu, so);
}
for(; j <= i; ++j)
{
  u[j] = c * u[j] + s * omegaCache[j];
}
Это было полезно?

Решение

Да, это отличный кандидат на векторизацию. Но, прежде чем сделать это, Убедитесь, что вы профилировали свой код Чтобы быть уверенным, что это на самом деле стоит оптимизировать. Тем не менее, векторизация пошла бы что-то вроде этого:

int i;
for(i = 0; i < n - 3; i += 4)
{
  load elements u[i,i+1,i+2,i+3]
  load elements b[i,i+1,i+2,i+3]
  vector multiply u * c
  vector multiply s * b
  add partial results
  store back to u[i,i+1,i+2,i+3]
}

// Finish up the uneven edge cases (or skip if you know n is a multiple of 4)
for( ; i < n; i++)
  u[i] = c * u[i] + s * b[i];

Для еще более производительности вы можете рассмотреть возможность предварительной выборки дополнительных элементов массива и / или развернуть петлю и использование Программное обеспечение Чтобы переместить вычисление в одну петлю с доступом памяти из другой итерации.

Другие советы

Вероятно, да, но вы должны помочь компилятору с некоторыми советами.__restrict__ Помещенные на указателях говорит компилятор, что нет псевдонимов между двумя указателями. Если вы знаете выравнивание ваших векторов, сообщите, что для компилятора (Visual C ++ может иметь некоторые объекты).

Я не знаком с Visual C ++ сам, но я слышал, что это не полезно для векторизации. Попробуйте использовать компилятор Intel вместо этого. Intel позволяет довольно мелкозернированный контроль над сборкой сборки: http://www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cpp_pragma_vector.htm.

_mm_set_pd не верится. Если принято буквально, он читает два двойных с использованием скалярных операций, затем сочетает в себе два скалярных удваивания и скопируют их в регистр SSE. Использовать _mm_load_pd вместо.

Да, это отличный кандидат на векторные кандидаты, предполагая, что нет перекрытия A и B Array. Но код связан с доступом памяти (Load / Store). Векторная видимость помогает уменьшить циклы на петлю, но инструкции будут стойвлять из-за пропускания кэша в массиве U и B. Компилятор Intel C / C ++ генерирует следующий код с флагами по умолчанию для процессора Xeon X5500. Компилятор разворачивает петлю на 8 и использует SIMD Add (AddPD) и Multipy (MULPD), используя XMM [0-16] CIMD-регистры. В каждом цикле процессор может выпускать 2 инструкции SIMD, что дает 4-х полос скалярных ILP, предполагая, что у вас есть данные, готовые в регистриях.

Здесь U, B, C и S - двойная точность (8 байтов).

    ..B1.14:                        # Preds ..B1.12 ..B1.10
    movaps    %xmm1, %xmm3                                  #5.1
    unpcklpd  %xmm3, %xmm3                                  #5.1
    movaps    %xmm0, %xmm2                                  #6.12
    unpcklpd  %xmm2, %xmm2                                  #6.12
      # LOE rax rcx rbx rbp rsi rdi r8 r12 r13 r14 r15 xmm0 xmm1 xmm2 xmm3
    ..B1.15:     # Preds ..B1.15 ..B1.14
    movsd     (%rsi,%rcx,8), %xmm4                          #6.21
    movhpd    8(%rsi,%rcx,8), %xmm4                         #6.21
    mulpd     %xmm2, %xmm4                                  #6.21
    movaps    (%rdi,%rcx,8), %xmm5                          #6.12
    mulpd     %xmm3, %xmm5                                  #6.12
    addpd     %xmm4, %xmm5                                  #6.21
    movaps    16(%rdi,%rcx,8), %xmm7                        #6.12
    movaps    32(%rdi,%rcx,8), %xmm9                        #6.12
    movaps    48(%rdi,%rcx,8), %xmm11                       #6.12
    movaps    %xmm5, (%rdi,%rcx,8)                          #6.3
    mulpd     %xmm3, %xmm7                                  #6.12
    mulpd     %xmm3, %xmm9                                  #6.12
    mulpd     %xmm3, %xmm11                                 #6.12
    movsd     16(%rsi,%rcx,8), %xmm6                        #6.21
    movhpd    24(%rsi,%rcx,8), %xmm6                        #6.21
    mulpd     %xmm2, %xmm6                                  #6.21
    addpd     %xmm6, %xmm7                                  #6.21
    movaps    %xmm7, 16(%rdi,%rcx,8)                        #6.3
    movsd     32(%rsi,%rcx,8), %xmm8                        #6.21
    movhpd    40(%rsi,%rcx,8), %xmm8                        #6.21
    mulpd     %xmm2, %xmm8                                  #6.21
    addpd     %xmm8, %xmm9                                  #6.21
    movaps    %xmm9, 32(%rdi,%rcx,8)                        #6.3
    movsd     48(%rsi,%rcx,8), %xmm10                       #6.21
    movhpd    56(%rsi,%rcx,8), %xmm10                       #6.21
    mulpd     %xmm2, %xmm10                                 #6.21
    addpd     %xmm10, %xmm11                                #6.21
    movaps    %xmm11, 48(%rdi,%rcx,8)                       #6.3
    addq      $8, %rcx                                      #5.1
    cmpq      %r8, %rcx                                     #5.1
    jl        ..B1.15       # Prob 99%                      #5.1

Это зависит от того, как вы разместили U и B в память. Если оба блока памяти далеко друг от друга, SSE не будет увеличиваться в этом сценарии.

Предполагается, что массив U и B представляют собой AoE (массив структуры) вместо SOA (структура массива), поскольку вы можете загрузить их обоих в регистр в одной инструкции.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top