Как использовать MMX mulH и mulL для двух 64-битных целых, чтобы получить одно 128-битное целое - PullRequest
1 голос
/ 19 февраля 2020

Здравствуйте, я работаю над еще одной целочисленной библиотекой произвольной точности. Я хотел реализовать умножение, но застрял, когда _m_pmulhw в <mmintrin.h> просто не сработало. очень мало документации по инструкциям MMX. Когда я проверяю его, он просто дает мне гиббери sh, когда я умножаю два UINT64_MAX.

uint_fast64_t mulH(const uint_fast64_t &a, const uint_fast64_t &b)  {  
    return (uint_fast64_t)_m_pmulhw((__m64)a,(__m64)b);
}
uint_fast64_t mulL(const uint_fast64_t &a, const uint_fast64_t &b)  {  
    return (uint_fast64_t)_m_pmullw((__m64)a,(__m64)b);
}
int main() {
    uint64_t a = UINT64_MAX;
    uint64_t b = UINT64_MAX;
    std::cout <<  std::bitset<64>(mulH(a,b)) << std::bitset<64>(mulL(a,b));
}

output: 00000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000100000000000000010000000000000001 

Я не знаю, почему он не работает, у меня есть APU A6-4400M ...

Вывод coreinfo : MMX * Supports MMX instruction set

Так что я думаю, что могу сказать, это не без поддержки. Если кто-нибудь может дать мне несколько советов о том, как заставить это работать, спасибо.

Компилятор: g cc

IDE: код Visual Studio

Ответы [ 2 ]

3 голосов
/ 19 февраля 2020

Я думаю, вы неправильно поняли, что делает _m_pmulhw. На самом деле это очень четко задокументировано в Intel Intrinsics Guide: https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text = _ m_pmulhw & expand = 4340 . Соответствующая инструкция - pmulhw, которая также четко документирована, например, в руководстве по инструкциям x86 Феликса Клотье: https://www.felixcloutier.com/x86/pmulhw

Она умножает четыре пары 16-битных целых чисел которые упакованы внутри двух операндов, а затем выдают верхнюю половину всех четырех умножений (High Packed Packed Multiply High-Word). Это означает, что для входов 0x12345678abcdef01, 0x9876543210fedcba он умножит 0x1234 * 0x9876, 0x5678 * 0x5432, 0xabcd * 0x10fe, 0xef01 * 0xdcba и упакует старшие 16 бит каждого результата в вывод.

Для вашего Например, вы умножаете 0xffff * 0xffff четыре раза, получая 32-битный результат 0x00000001 (-1 * -1, поскольку это 16-битное умножение со знаком), и, следовательно, получаете 0x0000000000000000 в старшей половине и 0x0001000100010001 в нижней половине - это именно то, что вы видите в выводе bitset.


Если вы ищете 128-битное умножение, на самом деле нет встроенного c для этого (кроме _mulx_u64, но при этом используется новая инструкция mulx, которая не так широко распространена). Microsoft имеет нестандартный _mul128 intrinsi c, но на других платформах вы можете просто использовать тип __int128 (или локальный эквивалент), чтобы получить 64x64 => 128-битное умножение.

Также Я бы настоятельно рекомендовал использовать набор инструкций SSE, а не старый набор MMX; Инструкции SSE в большинстве случаев быстрее и позволяют работать с гораздо более широкими векторными типами (теперь 256-битный стандарт, теперь доступен AVX512), что может обеспечить значительное повышение скорости.

2 голосов
/ 19 февраля 2020

Я не эксперт в этом, но согласно https://www.felixcloutier.com/x86/pmulhw, эти инструкции не делают умножение 64x64-> 128; они делают четыре умножения 16x16-> 32. Обратите внимание на слово «упаковано» в описании. Кроме того, это умножение со знаком.

Таким образом, ваши 64-битные значения UINT64_MAX интерпретируются как четыре слова 0xffff, то есть -1. Таким образом, вы умножаете -1 на -1 четыре раза. Конечно, числовой ответ на каждый из них 1. Результатом инструкции pmulhw являются старшие половины результатов (т.е. четыре слова 0x0000), а pmullw - младшие половины (то есть четыре слова 0x0001).

Это именно то, что вы получили, поэтому мне кажется, что инструкции работают отлично.

Если вы хотите сделать беззнаковое умножение двух 64-разрядных целых чисел, простая старомодная инструкция mul будет служить вашей цели, и самый простой способ получить gcc для ее генерации - это, вероятно, приведение ввод к __uint128_t и умножение с обычным оператором *.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...