Существует встроенная функция _mm_maskmoveu_si128
, которая переводится как maskmovdqu
(в SSE) или vmaskmovdqu
(в AVX).
// Store masks. The highest bit in each byte indicates the byte to store.
alignas(16) const unsigned char masks[16][16] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }
};
void store_n(__m128i mm, unsigned int n, void* storage)
{
assert(n < 16u);
_mm_maskmoveu_si128(mm, reinterpret_cast< const __m128i& >(masks[n]), static_cast< char* >(storage));
}
Проблема с этим кодом заключается в том, что инструкции maskmovdqu
(и, предположительно, vmaskmovdqu
) имеют связанную подсказку для невременного доступа к целевой памяти, что делает инструкцию дорогостоящей, а также требует впоследствии ограждения.
AVX добавляет новые инструкции vmaskmovps
/vmaskmovpd
(и AVX2 также добавляет vpmaskmovd
/vpmaskmovq
), которые работают аналогично vmaskmovdqu
, но не имеют невременной подсказки и работают только с 32- и 64-битной детализацией.
// Store masks. The highest bit in each 32-bit element indicates the element to store.
alignas(16) const unsigned char masks[4][16] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 }
};
void store_n(__m128i mm, unsigned int n, void* storage)
{
assert(n < 4u);
_mm_maskstore_epi32(static_cast< int* >(storage), reinterpret_cast< const __m128i& >(masks[n]), mm);
}
AVX-512 добавляет маскированные хранилища, и вы можете использовать vmovdqu8
/vmovdqu16
с соответствующей маской для хранения 8- или 16-битных элементов.
void store_n(__m128i mm, unsigned int n, void* storage)
{
assert(n < 16u);
_mm_mask_storeu_epi8(storage, static_cast< __mmask16 >((1u << n) - 1u), mm);
}
Обратите внимание, что для вышеуказанного требуются расширения AVX-512BW и VL.
Если вам требуется 8- или 16-битная гранулярность и у вас нет AVX-512, вам лучше использовать функцию, которая вручную сохраняет векторный регистр по частям.
void store_n(__m128i mm, unsigned int n, void* storage)
{
assert(n < 16u);
unsigned char* p = static_cast< unsigned char* >(storage);
if (n >= 8u)
{
_mm_storel_epi64(reinterpret_cast< __m128i* >(p), mm);
mm = _mm_unpackhi_epi64(mm, mm); // move high 8 bytes to the low 8 bytes
n -= 8u;
p += 8;
}
if (n >= 4u)
{
std::uint32_t data = _mm_cvtsi128_si32(mm);
std::memcpy(p, &data, sizeof(data)); // typically generates movd
mm = _mm_srli_si128(mm, 4);
n -= 4u;
p += 4;
}
if (n >= 2u)
{
std::uint16_t data = _mm_extract_epi16(mm, 0); // or _mm_cvtsi128_si32
std::memcpy(p, &data, sizeof(data));
mm = _mm_srli_si128(mm, 2);
n -= 2u;
p += 2;
}
if (n > 0u)
{
std::uint32_t data = _mm_cvtsi128_si32(mm);
*p = static_cast< std::uint8_t >(data);
}
}
person
Andrey Semashev
schedule
30.08.2020
__m128i
является союзом. - person ALX23z   schedule 30.08.2020movq
иmovd
, которые могут хранить 8 или 4 байта. Или, если длина smallArray составляет не менее 16, вы можете сделать невыровненный конечный вектор, если вы организуете свой цикл, чтобы оставить результат в переменной, которая будет сохранена на следующей итерации (или при выходе из цикла, после загрузки данных для потенциально перекрывающихся невыровненных конечный вектор). - person Peter Cordes   schedule 30.08.2020