Как найти горизонтальный максимум в 256-битном AVX-векторе

У меня есть вектор __m256d, упакованный с четырьмя 64-битными значениями с плавающей запятой.
Мне нужно найти горизонтальный максимум векторных элементов и сохранить результат в скалярном значении с двойной точностью;

Мои попытки все закончились тем, что много перетасовки векторных элементов, делая код не очень элегантным и эффективным. Кроме того, я не мог оставаться в домене AVX. В какой-то момент мне пришлось использовать SSE 128-битные инструкции для извлечения окончательного 64-битного значения. Тем не менее, я хотел бы, чтобы в этом последнем утверждении было ошибочно.

Итак, идеальное решение будет:
1) используйте только инструкции AVX.
2) свести к минимуму количество инструкций. (Я надеюсь, что не более 3-4 инструкций)

Сказав это, любое элегантное/эффективное решение будет принято, даже если оно не соответствует приведенным выше рекомендациям.

Спасибо за любую помощь.

-Luigi

Ответ 1

Я не думаю, что вы можете сделать намного лучше, чем 4 инструкции: 2 тасования и 2 сравнения.

__m256d x = ...; // input

__m128d y = _mm256_extractf128_pd(x, 1); // extract x[2], and x[3]
__m128d m1 = _mm_max_pd(x, y); // m1[0] = max(x[0], x[2]), m1[1] = max(x[1], x[3])
__m128d m2 = _mm_permute_pd(m1, 1); // set m2[0] = m1[1], m2[1] = m1[0]
__m128d m = _mm_max_pd(m1, m2); // both m[0] and m[1] contain the horizontal max(x[0], x[1], x[2], x[3])

Тривиальная модификация для работы только с 256-битными векторами:

__m256d x = ...; // input

__m256d y = _mm256_permute2f128_pd(x, x, 1); // permute 128-bit values
__m256d m1 = _mm256_max_pd(x, y); // m1[0] = max(x[0], x[2]), m1[1] = max(x[1], x[3]), etc.
__m256d m2 = _mm256_permute_pd(m1, 5); // set m2[0] = m1[1], m2[1] = m1[0], etc.
__m256d m = _mm256_max_pd(m1, m2); // all m[0] ... m[3] contain the horizontal max(x[0], x[1], x[2], x[3])

(непроверенные)

Ответ 2

Общий способ сделать это для вектора v1 = [A, B, C, D] равен

  • Переместить v1 в v2 = [C, D, A, B] (заменить 0-й и 2-й элементы, 1-й и 3-й)
  • Возьмите max; т.е. v3 = max(v1,v2). Теперь у вас есть [max(A,C), max(B,D), max(A,C), max(B,D)]
  • Переместить v3 в v4, заменив 0-й и 1-й элементы, 2-й и 3-й.
  • Возьмите max, т.е. v5 = max(v3,v4). Теперь v5 содержит горизонтальный макс во всех его компонентах.

В частности, для AVX перестановки можно выполнять с помощью _mm256_permute_pd, а максимальные значения можно выполнить с помощью _mm256_max_pd. У меня нет точных перестановочных масок, но они должны быть достаточно понятны.

Надеюсь, что это поможет.

Ответ 3

//Use the code to find the horizontal maximum
__m256 v1 = initial_vector;//example v1=[1 2 3 4 5 6 7 8]
__m256 v2 = _mm256_permute_ps(v1,(int)147);//147 is control code for rotate left by upper 4 elements and lower 4 elements separately v2=[2 3 4 1 6 7 8 5]
__m256 v3 = _mm256_max_ps(v1,v2);//v3=[2 3 4 4 6 7 8 8]
__m256 v4 = _mm256_permute_ps(v3,(int)147);//v4=[3 4 4 2 7 8 8 6]
__m256 v5 = _mm256_max_ps(v3,v4);//v5=[3 4 4 4 7 8 8 8]
__m256 v6 = _mm256_permute_ps(v5,(int)147);//v6=[4 4 4 3 8 8 8 7]
__m256 v7 = _mm256_max_ps(v5,v6);//contains max of upper four elements and lower 4 elements. v7=[4 4 4 4 8 8 8 8]

//to get max of this horizontal array. Note that either upper or lower can contain the maximum
float ALIGN max_array[8];
float horizontal_max;
_mm256_store_ps(max_array, v7);
if(max_array[0] > max_array[7])
{
    horizontal_max = max_array[0];
}
else
{
    horizontal_max = max_array[7];
}