CLMUL instruction set w c++

0

Napisałem funkcję mnożenia w Galois fields w c++, ale jest ona wolna, zwłaszcza w porównaniu do normalnego mnożenia:

uint32_t p = 4194311;

uint32_t add( uint32_t a, uint32_t b )
{
    return a ^ b;
}

uint32_t multiply( uint32_t a, uint32_t b )
{
    uint64_t c = 0;

    for( unsigned int i = 0; i < 32; ++i )
    {
        c ^= a & ( 1 << i ) ? (uint64_t) b << i : 0;
    }

    for( unsigned int i = 32; i --> 0 ; )
    {
        if( c & ( 1 << ( i + 32 ) ) )
        {
            c ^= 1 << ( i + 32 );
            c ^= (uint64_t) p << i;
        }
    }

    return (uint32_t) c;
}

Co więcej chciałbym na przykład mnożyć w GF liczby 64-bitowe albo 128-bitowe, a tego nie da się zrobić w mojej funkcji bez intów 128-bitowych i 256-bitowych. Zaś CLMUL chyba pozwala mnożyć nawet tak duże liczby w GF.

Czy wiecie jak użyć tych instrukcji w praktyce?

0

Jak masz bardzo silną opinię na temat tego, jakich dokładnie instrukcji użyć, to zawsze możesz pisać swoje wstawki assemblerowe — chociaż nie jest to część oficjalnego standardu, większość kompilatorów bez problemu to przełknie. https://en.cppreference.com/w/cpp/language/asm

0

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.

1 użytkowników online, w tym zalogowanych: 0, gości: 1