Встроенная сборка операции сокращения для 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

Я думаю, вы должны быть в состоянии найти свой путь в этом...

Другие вопросы по тегам