Встроенная сборка операции сокращения для Xeon Phi
Я ищу встроенную операцию сборки для операции уменьшения и уменьшения Xeon Phi. Я нашел _mm512_reduce_add_epi32 встроенным на встроенном веб-сайте Intel ( ссылка). Однако на сайте они не упомянули фактическую операцию сборки для него.
Кто-нибудь может помочь мне найти встроенную сборку операции редукции на платформе Xeon Phi?
Спасибо
2 ответа
Сокращение на 16 целых чисел с помощью KNC является интересным случаем, чтобы показать, почему он отличается от AVX512.
_mm512_reduce_add_epi32
intrinsic поддерживается только компиляцией Intel (в настоящее время). Это одна из тех раздражающих многих встроенных команд, как в SVML. Но я думаю, что понимаю, почему Intel реализовала эту встроенную функцию, как в этом случае, потому что результаты для KNC и AVX512 очень разные.
С AVX512 я бы сделал что-то подобное
__m256i hi8 = _mm512_extracti64x4_epi64(a,1);
__m256i lo8 = _mm512_castsi512_si256(a);
__m256i vsum1 = _mm256_add_epi32(hi8,lo8);
и тогда я бы сделал сокращение так же, как в AVX2
__m256i vsum2 = _mm256_hadd_epi32(vsum1,vsum1);
__m256i vsum3 = _mm256_hadd_epi32(vsum2,vsum2);
__m128i hi4 = _mm256_extracti128_si256(vsum3,1);
__m128i lo4 = _mm256_castsi256_si128(vsum3);
__m128i vsum4 = _mm_add_epi32(hi4, lo4);
int sum = _mm_cvtsi128_si32(vsum4);
Было бы интересно посмотреть, как Intel реализует _mm512_reduce_add_epi32
с AVX512.
Но набор команд KNC не поддерживает AVX или SSE, поэтому все должно быть сделано с полными 512-битными векторами с KNC. Intel создала инструкции, уникальные для KNC, чтобы сделать это.
Глядя на сборку ответа Джайлза, мы видим, что он делает. Сначала он переставляет старшие 256 бит в младшие 256 бит, используя уникальную для KNC инструкцию, например:
vpermf32x4 $238, %zmm0, %zmm1
Значение 238
является 3232
в базе 4. Итак zmm1
с точки зрения четырех 128-битных линий (3,2,3,2)
,
Далее это делает векторную сумму
vpaddd %zmm0, %zmm1, %zmm3
что дает четыре 128-битных дорожек (3+3, 2+2, 3+1, 2+0)
Затем он переставляет второй 128-битный канал (3+1, 3+1, 3+1, 3+1)
как это
vpermf32x4 $85, %zmm3, %zmm2
где 85
является 1111
в базе 4. Затем он складывает их вместе
vpaddd %zmm3, %zmm2, %zmm4
так что нижняя 128-битная дорожка в zmm4
содержит сумму четырех 128-битных дорожек (3+2+1+0)
,
На этом этапе необходимо переставить 32-битные значения в каждой 128-битной полосе. Опять же, он использует уникальную особенность KNC, которая позволяет ему переставлять и добавлять одновременно (или, по крайней мере, запись является уникальной).
vpaddd %zmm4{badc}, %zmm4, %zmm5
производит (a+b, a+b, c+d, c+d)
а также
vpaddd %zmm5{cdab}, %zmm5, %zmm6
производит (a+b+c+d , a+b+c+d , a+b+c+d, a+b+c+d)
, Теперь это просто вопрос извлечения младших 32-битных.
Вот альтернативное решение для AVX512, которое похоже на решение для KNC
#include <x86intrin.h>
int foo(__m512i a) {
__m512i vsum1 = _mm512_add_epi32(a,_mm512_shuffle_i64x2(a,a, 0xee));
__m512i vsum2 = _mm512_add_epi32(a,_mm512_shuffle_i64x2(vsum1,vsum1, 0x55));
__m512i vsum3 = _mm512_add_epi32(a,_mm512_shuffle_epi32(vsum2, _MM_PERM_BADC));
__m512i vsum4 = _mm512_add_epi32(a,_mm512_shuffle_epi32(vsum3, _MM_PERM_CADB));
return _mm_cvtsi128_si32(_mm512_castsi512_si128(vsum4));
}
С gcc -O3 -mavx512f
это дает.
vshufi64x2 $238, %zmm0, %zmm0, %zmm1
vpaddd %zmm1, %zmm0, %zmm1
vshufi64x2 $85, %zmm1, %zmm1, %zmm1
vpaddd %zmm1, %zmm0, %zmm1
vpshufd $78, %zmm1, %zmm1
vpaddd %zmm0, %zmm1, %zmm1
vpshufd $141, %zmm1, %zmm1
vpaddd %zmm0, %zmm1, %zmm0
vmovd %xmm0, %eax
ret
AVX512 использует vshufi64x2
вместо vpermf32x4
и KNC объединяет перестановку внутри дорожек и добавление с нотацией {abcd} (например, vpaddd %zmm4{badc}, %zmm4, %zmm5
). Это в основном то, что достигается с помощью _mm256_hadd_epi32
,
Я забыл, что уже видел этот вопрос для AVX512. Вот еще одно решение.
То, что здесь стоит, это встроенные (не проверенные) для KNC.
int foo(__m512i a) {
__m512i vsum1 = _mm512_add_epi32(a,_mm512_permute4f128_epi32(a, 0xee));
__m512i vsum2 = _mm512_add_epi32(a,_mm512_permute4f128_epi32(vsum1, 0x55));
__m512i vsum3 = _mm512_add_epi32(a,_mm512_swizzle_epi32(vsum2, _MM_SWIZ_REG_BADC));
__m512i vsum4 = _mm512_add_epi32(a,_mm512_swizzle_epi32(vsum3, _MM_SWIZ_REG_CADB));
int32_t out[2];
_mm512_packstorelo_epi32(out, vsum4);
return out[0];
}
Я не вижу разницы в функциональности между KNC _mm512_permute4f128_epi32(a,imm8
) и AVX512 _mm512_shuffle_i32x4(a,a,imm8)
,
Основное отличие в этом случае заключается в том, что _mm512_shuffle_epi32
генерирует vpshufd
в то время как _mm512_swizzle_epi32
не. Это, кажется, преимущество KNC перед AVX512.
Я почти ничего не знаю, когда дело доходит до чтения ассемблера, поэтому я просто сделал это:
Создал файл foo.c примерно так:
#include "immintrin.h"
int foo(__m512i a) {
return _mm512_reduce_add_epi32(a);
}
Который я выполнил с компилятором Intel версии 16.0.1, используя -mmic -S
, И это дало мне следующий код сборки:
# -- Begin foo
.text
# mark_begin;
# Threads 4
.align 16,0x90
.globl foo
# --- foo(__m512i)
foo:
# parameter 1: %zmm0
..B1.1: # Preds ..B1.0 Latency 53
.cfi_startproc
..___tag_value_foo.1:
..L2:
#3.20
movl $1, %eax #4.12 c1
vpermf32x4 $238, %zmm0, %zmm1 #4.12 c5
kmov %eax, %k1 #4.12 c5
vpaddd %zmm0, %zmm1, %zmm3 #4.12 c9
nop #4.12 c13
vpermf32x4 $85, %zmm3, %zmm2 #4.12 c17
vpaddd %zmm3, %zmm2, %zmm4 #4.12 c21
nop #4.12 c25
vpaddd %zmm4{badc}, %zmm4, %zmm5 #4.12 c29
nop #4.12 c33
vpaddd %zmm5{cdab}, %zmm5, %zmm6 #4.12 c37
nop #4.12 c41
vpackstorelps %zmm6, -8(%rsp){%k1} #4.12 c45
movl -8(%rsp), %eax #4.12 c49
ret #4.12 c53
.align 16,0x90
.cfi_endproc
# LOE
# mark_end;
.type foo,@function
.size foo,.-foo
.data
# -- End foo
.data
.section .note.GNU-stack, ""
// -- Begin DWARF2 SEGMENT .eh_frame
.section .eh_frame,"a",@progbits
.eh_frame_seg:
.align 8
# End
Я думаю, вы должны быть в состоянии найти свой путь в этом...