To co udało mi się zrobić na podstawie kodów stąd:
https://wunkolo.github.io/post/2020/05/pclmulqdq-tricks/
To użycie instrukcji do carry-less multiplication dwóch liczb 64-bitowych. Wygląda to tak:
#include <immintrin.h>
uint64_t multiply(uint64_t a, uint64_t b)
{
uint64_t c = 0;
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0, a), _mm_set_epi64x(0, b), 0);
c = _mm_cvtsi128_si64(result);
return c;
}
Mniejsze liczby niż 64-bitowe też można mnożyć, choć chyba nie ma dedykowanej instrukcji do tego (nie wiem), a może byłaby szybsza, gdyby była. Nie wiem, czy można mnożyć liczby większe niż 64-bitowe w ramach tych instrukcji. Żeby działać na 128-bitowych intach i w ogóle zadeklarować 128-bitowe liczby też zdaje się trzeba specjalnych instrukcji SSE, plus właśnie pclmulqdq.
I co najważniejsze - działa to podobnie szybko jak normalne mnożenie, przynajmniej w praktycznych zastosowaniach, nie mierzyłem prędkości samego kodu, patrzę jak szybko wyniki całego programu pipelajnują mi się do terminala (tak to się nazywa?). Wcześniej działało to 3-4 razy wolniej niż zwykłe mnożenie.
Ale to nie wszystko. Jak na razie szybciej wykonuje mi się tylko carry-less multiplication, tymczasem mnożenie w GF(2^k) wymaga jeszcze skrócenia wyniku modulo pewien wielomian. Nie wiem, czy CLMUL instruction set na to pozwala, ale chyba nie. Z tego co czytam tu:
https://en.wikipedia.org/wiki/CLMUL_instruction_set
mnożenie dwóch 64-bitowych liczb, to jest wszystko, co ten zestaw daje.