(Примечание редактора: изначально этот вопрос был: Как можно получить доступ к члену m128i_i8 или членам в целом объекта __m128i? , пытаясь использовать специфичный для MSVC метод в определении GCC для {{X0 }}. Но это была проблема XY, и принятый ответ касается проблемы XY здесь. Другой ответ действительно отвечает на этот вопрос.)
Я понимаю, что Microsoft предлагает отказаться от прямого доступа к членам этих объектов, но мне нужно установить их и документации крайне не хватает.
Я продолжаю получать сообщение об ошибке «запрос члена 'm128i_i8' в '(мое имя переменной)', который не относится к типу класса 'wirelabel {aka __vector (2) long long int}'», которого я не понимаю, потому что я включил все правильные заголовки и распознает переменные __m128i.
Примечание 1: wirelabel - это typedef для __m128i, т.е. существует в заголовке
typedef __m128i wirelabel
Note2: причина использования Note1 объясняется в следующем вопросе: tbb :: cache_aligned_allocator: получение "запроса для члена ... неклассового типа с __m128i. Ошибка пользователя или ошибка?
Примечание 3: я использую компилятор g ++
Примечание 4: этот следующий вопрос не отвечает на мой вопрос, но обсуждает связанную информацию Почему вам не следует напрямую обращаться к полям __m128i?
Я также знаю, что есть функция _mm_set_epi8, но она требует, чтобы вы установили сразу все 8-битные разделы, и в настоящее время это не вариант для меня.
На вопрос принятый ответ отвечает:
Изменить: меня попросили уточнить, почему я думаю, что мне нужно получить доступ к каждой из 16 8-битных частей объекта __m128i
, и вот почему: у меня есть массив bool
с размером 'n * 128' (n - size_t), и мне нужно сохранить их в массиве 'wirelabel' с размером 'n'.
Теперь, поскольку wirelabel - это просто псевдоним / typedef (поправьте меня, если есть разница) для __m128i, каждый из индексов 'n' из 128 bool может быть сохранен в массиве 'wirelabel'.
Однако, чтобы сделать это, я считаю, что необходимо преобразовать каждые 8 бит в его эквивалент со знаком и сохранить его в правильном 8-битном индексе в каждом указателе 'wirelabel' в массиве.
2 ответа
Значит, ваши исходные данные непрерывны? Вам следует использовать _mm_load_si128
вместо того, чтобы возиться со скалярными компонентами векторных типов.
Ваша настоящая проблема заключается в упаковке массива bool
(1 байт на элемент в ABI, используемом g ++ на x86) в растровое изображение. Вы должны делать это с SIMD, а не со скалярным кодом, чтобы установить 1 бит или байт за раз.
pmovmskb
(_mm_movemask_epi8
) отлично подходит для извлечения одного бита на байт ввода. Вам просто нужно расположить так, чтобы нужный бит оказался в высоком.
Очевидным выбором был бы сдвиг, но инструкции векторного сдвига конкурируют за тот же порт выполнения, что и pmovmskb
на Haswell (порт 0). (http://agner.org/optimize/). Вместо этого добавление 0x7F
создаст 0x80
(набор старших битов) для ввода 1
, но 0x7F
(сброс старших битов) для ввода 0
. (И bool
в x86-64 System V ABI должен храниться в памяти как целое число 0 или 1, а не просто 0 по сравнению с любым ненулевым значением).
Почему не pcmpeqb
против _mm_set1_epi8(1)
? Skylake работает pcmpeqb
на портах 0/1, но paddb
на всех трех векторных портах ALU (0/1/5). Однако очень часто используется pmovmskb
в результате pcmpeqb/w/d/q
.
#include <immintrin.h>
#include <stdint.h>
// n is the number of uint16_t dst elements
// We access n*16 bool elements from src.
void pack_bools(uint16_t *dst, const bool *src, size_t n)
{
// you can later access dst with __m128i loads/stores
__m128i carry_to_highbit = _mm_set1_epi8(0x7F);
for (size_t i = 0 ; i < n ; i+=1) {
__m128i boolvec = _mm_loadu_si128( (__m128i*)&src[i*16] );
__m128i highbits = _mm_add_epi8(boolvec, carry_to_highbit);
dst[i] = _mm_movemask_epi8(highbits);
}
}
Поскольку при записи этого растрового изображения мы хотим использовать скалярные хранилища, мы хотим, чтобы dst
находился в uint16_t
по причинам строгого сглаживания. С AVX2 вам понадобится uint32_t
. (Или если вы сделали combine = tmp1 << 16 | tmp
для объединения двух результатов pmovmskb
. Но, вероятно, не делайте этого.)
Это компилируется в такой цикл asm (с gcc7.3 -O3 в проводнике компилятора Godbolt)
.L3:
movdqu xmm0, XMMWORD PTR [rsi]
add rsi, 16
add rdi, 2
paddb xmm0, xmm1
pmovmskb eax, xmm0
mov WORD PTR [rdi-2], ax
cmp rdx, rsi
jne .L3
Так что это не замечательно (7 мопов fuse-domain -> узкое место переднего конца при 16 bool за ~ 1,75 такта). Clang разворачивается на 2 и должен обрабатывать 16 булов за 1,5 цикла.
Использование сдвига (pslld xmm0, 7
) будет выполняться только на одной итерации за 2 цикла на Haswell, узким местом является порт 0.
Создайте анонимное объединение, содержащее член _m128i
и массив другого типа, члены которого вы хотите установить. Воспроизведение типов разрешено в C и поддерживается как расширение в g ++, clang ++ и MSVC. Если вы хотите установить отдельные биты, вы можете объявить другой член как struct
битовых полей. Порядок битового поля определяется реализацией, но вы все равно используете встроенный Intel, поэтому он будет прямым порядком байтов.
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .