Как загрузить 96 бит из памяти в регистр XMM?
Скажем, у меня есть указатель на память в rsi
и я хотел бы загрузить 12-байтовое значение, указанное в младшие 96 бит xmm0
, Мне все равно, что происходит с старшими 32 битами. Какой эффективный способ сделать это?
(Дополнительный вопрос: лучшее, что я придумал, включает в себя movlpd
Инструкция "Переместить низкоупакованное значение с плавающей запятой двойной точности". Есть ли способ, которым эти инструкции относятся к значениям с плавающей точкой? Я не понимаю, что это задокументировано таким образом; конечно, это должно работать и для целых чисел.)
3 ответа
Если загрузка 16 байт не переходит на другую страницу и происходит сбой, используйте movups
, Старшие 4 байта будут тем мусором, который есть в памяти. Причиной пропуска кэша для 4B, который вас не волнует, может быть проблема, как и разделение строки кэша.
В противном случае используйте movq
/ pinsrd
(SSE4.1) или каким-либо другим способом выполнения двух загрузок + перемешивание. movq
+ pinsrd
на процессорах семейства Intel SnB будет 3 мопа с плавким доменом, потому что pinsrd
не может микро-предохранитель. (А для его ALU uop требуется порт для воспроизведения в случайном порядке (p5)).
Другая возможность: AVX VMASKMOVPS xmm1, xmm2, m128
,
Условно перемещает элементы упакованных данных из второго операнда источника в соответствующий элемент данных операнда назначения в зависимости от битов маски, связанных с каждым элементом данных (MSB 1-го операнда src).
... Ошибки не возникнут из-за ссылки на какую-либо ячейку памяти, если соответствующий бит маски для этой ячейки памяти равен 0.
Intel Haswell: 3 моноблока с плавкими доменами (одна загрузка и две в случайном порядке (p5)). Задержка 4c, по одному на пропускную способность 2c.
Это, вероятно, не очень хорошо по сравнению, особенно если окружающий код должен перетасовать.
Ваша очень редко используемая условная ветвь, которая использует movups
в любое время гарантируется, что он не будет неисправен, это также 3 мопа слитых доменов на быстром пути, и один из них может работать на порту 6 (вообще не конкурируя с векторными ALU). LEA тоже не на критическом пути.
movlpd
безопасно использовать на любых данных. Он никогда не будет ошибочным или медленным с данными, которые представляют NaN с плавающей запятой, или чем-то подобным. Вам следует беспокоиться об этом только в инструкциях, перечисленных в руководстве по insn с непустым разделом "Исключения с плавающей точкой SIMD". например addps
может генерировать исключения "Переполнение, Недостаточный, Неверный, Точность, Ненормальный", но shufps
говорит "Нет".
Ответ Питера Кордеса помог мне заставить задуматься о страницах, и я просто проверил, есть ли шанс, что мы ошибемся:
// We'd like to perform only a single load from memory, but there's no 96-bit
// load instruction and it's not necessarily safe to load the full 128 bits
// since this may read beyond the end of the buffer.
//
// However, observe that memory protection applies with granularity of at
// most 4 KiB (the smallest page size). If the full 16 bytes lies within a
// single 4 KiB page, then we're fine. If the 12 bytes we are to read
// straddles a page boundary, then we're also fine (because the next four
// bytes must lie in the second page, which we're already reading). The only
// time we're not guaranteed to be okay to read 16 bytes is if the 12 bytes
// we want to read lie near the end of one page, and some or all of the
// following four bytes lie within the next page.
//
// In other words, the only time there's a risk is when the pointer mod 4096
// is in the range [4081, 4085). This is <0.1% of addresses. Check for this
// and handle it specially.
//
// We perform the check by adding 15 and then checking for the range [0, 3).
lea rax, [rsi+15]
test eax, 0xffc
jz slow_read
// Hooray, we can load from memory just once.
movdqu xmm0, XMMWORD PTR [rsi]
done_reading:
[...]
slow_read:
movq xmm1, QWORD PTR [rsi]
pinsrd xmm1, DWORD PTR [rsi+8], 2
jmp done_reading
movss xmm0, [rdx+8] //; +8*8Bits = 64 Bits
pshufd xmm0, xmm0, 0x00 //; spreading it in every part
movlps xmm0, [rdx] //; overwriting the lower with 64 Bits
В моем случае это сработало с Float, не уверен, что вам это подходит.