У меня есть некоторый код в цикле
for(int i = 0; i < n; i++)
{
u[i] = c * u[i] + s * b[i];
}
Так, u и b являются векторами той же длины, и c и s являются скалярами. Действительно ли этот код является хорошим кандидатом на векторизацию для использования с SSE для получения ускорения?
ОБНОВЛЕНИЕ
Я учился, векторизация (оказывается, что не настолько трудно, если Вы используете intrinsics), и реализовал мой цикл в 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];
Для еще большей производительности вы можете рассмотреть возможность предварительной выборки последующих элементов массива, и/или развернуть цикл и использовать программную конвейеризацию для чередования вычислений в одном цикле с обращениями к памяти из другой итерации.
это зависит от того, как вы поместили u и b в память. если оба блока памяти находятся далеко друг от друга, SSE не будет сильно увеличиваться в этом сценарии.
предлагается, чтобы массивы u и b были AOE (массив структур) вместо SOA (структура массива), потому что вы можете загрузить их оба в регистр в одной инструкции.
Возможно, да, но вы должны помочь компилятору некоторыми подсказками.
__ restrict __
, помещенное в указатели, сообщает компилятору, что между двумя указателями нет псевдонима.
если вы знаете выравнивание ваших векторов, сообщите об этом компилятору (Visual C ++ может иметь некоторую возможность).
Я сам не знаком с Visual C ++, но слышал, что он не годится для векторизации. Вместо этого рассмотрите возможность использования компилятора Intel. Intel позволяет довольно детально управлять созданной сборкой: http://www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cppref_pragma_vector.htm
_mm_set_pd
не векторизован. Если воспринимать ее буквально, она считывает два дубля, используя скалярные операции, затем объединяет два скалярных дубля и копирует их в регистр SSE. Вместо этого используйте _mm_load_pd
.