Я пытался понять, как мы должны разбросать 16-битные целые числа, используя инструкции разброса в AVX512. У меня есть 8 x 16-битных целых чисел, хранящихся по одному в каждом из 32-битных целых чисел __m256i. Я бы использовал 256-битный эквивалент _mm512_i32extscatter_epi32, понижающий преобразование _MM_DOWNCONV_EPI32_UINT16, но такой инструкции нет, и понижающее преобразование не работает на AVX512.
Насколько я понимаю, мы должны выполнять 32-битные операции чтения и записи, и мы должны быть осторожны, чтобы две соседние 16-битные записи уничтожали друг друга (если один и тот же индекс в списке индексов дважды, тогда я не не нужно беспокоиться о том, что произойдет раньше). Таким образом, мы должны использовать цикл разброса при сборе данных о конфликтах. В цикле мы должны конфликтовать с 32-битными целочисленными адресами или с 16-битными индексами, сдвинутыми влево на 1 и используемыми в качестве индексов для эквивалентного 32-битного массива (эквивалент преобразования 16-битного массива в 32-битный массив). array, а затем разделив индекс на 2). Затем нам нужно взять 32-битное целое число, которое мы читаем, и изменить старшие 16 бит или младшие 16 бит в зависимости от того, был ли исходный индекс в 16-битном массиве нечетным или четным.
Итак, вот что я получаю:
Определите, являются ли индексы нечетными или четными, и установите 2-битную маску 01 или 10 соответственно, образуя 16-битную маску для 8 целых чисел.
Превратите 16-битные целые числа в 32-битные целые числа, скопировав младшие 16 бит в старшие 16 бит.
Превратите индекс в массив 16-битных целых чисел в индекс в массив 32-битных индексов, сдвинувшись вправо на единицу.
Используйте конфликтную петлю с маской
32-битные целые числа с маскированной сборкой
Используйте _mm256_mask_blend_epi16, чтобы выбрать, следует ли изменять старшие или младшие 16 бит только что прочитанных 32-битных целых чисел (используя маску из (1)).
Замаскированный-рассеянный обратно в память
повторять до тех пор, пока у нас не будет конфликтов в незаписанных 32-битных целочисленных адресах.
Пожалуйста, есть ли более быстрый (или более простой) способ сделать это? И да, я знаю, что отдельные записи выполняются быстрее, но речь идет о том, как это сделать с помощью AVX-512.
Вот код:
void scatter(uint16_t *array, __m256i vindex, __m256i a)
{
__mmask16 odd = _mm256_test_epi16_mask(vindex, _mm256_set1_epi32(1));
__mmask16 even = ~odd & 0x5555;
__mmask16 odd_even = odd << 1 | even;
__m256i data = _mm256_mask_blend_epi16(0x5555, _mm256_bslli_epi128(a, 2), a);
__m256i word_locations = _mm256_srli_epi32(vindex, 1);
__mmask8 unwritten = 0xFF;
do
{
__m256i conflict = _mm256_maskz_conflict_epi32 (unwritten, word_locations);
conflict = _mm256_and_si256(_mm256_set1_epi32(unwritten), conflict);
__mmask8 mask = unwritten & _mm256_testn_epi32_mask(conflict, _mm256_set1_epi32(0xFFFF'FFFF));
__m256i was = _mm256_mmask_i32gather_epi32(_mm256_setzero_si256(), mask, word_locations, array, 4);
__m256i send = _mm256_mask_blend_epi16(odd_even, was, data);
_mm256_mask_i32scatter_epi32(array, mask, word_locations, send, 4);
unwritten ^= mask;
}
while (unwritten != 0);
}