2011-10-24 10 views
6

Jaki jest najbardziej efektywny sposób na 128-bitowe przesunięcie na nowoczesnym procesorze Intel (rdzeń i7, piaszczysty most).128-bitowe przesunięcia przy użyciu assemblera?

Podobny kod jest moim najbardziej wewnętrznej pętli:

u128 a[N]; 
void xor() { 
    for (int i = 0; i < N; ++i) { 
    a[i] = a[i]^(a[i] >> 1)^(a[i] >> 2); 
    } 
} 

Dane w a[N] jest prawie przypadkowa.

+0

64 bit lub 32 bit? –

+1

Można zacząć od włączenia maksymalnej optymalizacji i sprawdzenia, co generuje kompilator. –

+0

Czy możesz pokazać nam definicję 'u128'? Prawdopodobnie mogę zapewnić wydajne rozwiązanie za pomocą SSE. – Mysticial

Odpowiedz

9

Korzystanie z instrukcji Shift Double.

Tak więc lub SHRD instrukcja, ponieważ SSE nie jest przeznaczony do tego celu. Istnieje klasyczna metoda, tutaj masz przypadki testowe dla 128-bitowego przesunięcia w lewo o 16 bitów w trybie procesora 32-bitowego i 64-bitowego.

W ten sposób można wykonać nieograniczoną zmianę rozmiaru dla maksymalnie 32/64 bitów. Yoo może przesuwać w kierunku liczby bitów lub liczby w rejestrze kl. Pierwsza instrukcja operant może również adresować zmienną w pamięci.

128 bitów w lewo przesunięcie o 16 bitach na podstawie 32-bitowym trybie x86 procesor:

mov  eax, $04030201; 
    mov  ebx, $08070605; 
    mov  ecx, $0C0B0A09; 
    mov  edx, $100F0E0D; 

    shld edx, ecx, 16 
    shld ecx, ebx, 16 
    shld ebx, eax, 16 
    shl  eax, 16 

i 128 bit przesunięcia w lewo o 16 bitów na podstawie 64-bitowym trybie x86 procesor:

mov rax, $0807060504030201; 
    mov rdx, $100F0D0E0B0C0A09; 

    shld rdx, rax, 16 
    shl rax, 16 
+1

Użyłem tego. Działa i jest dość szybki, ale powinieneś wspomnieć, że 32-bitowy kod umożliwia przesunięcie do 31, a 64-bitowy kod do 63. Jeśli chcesz przesunąć o zmienną wielkość, której nie można zagwarantować, że jest mniejszy niż 64, nie można tego użyć. – hirschhornsalz

+0

@drhirsch: Wspomniałem do 32/64 bitów i oczywiście powinno być do 31/63bits, jeśli chcesz więcej niż przenieść 32/64-bitowe słowa. –

3

W tym W szczególnym przypadku możesz użyć kombinacji instrukcji x86 SHR i RCR:

; a0 - bits 0-31 of a[i] 
; a1 - bits 32-63 of a[i] 
; a2 - bits 64-95 of a[i] 
; a3 - bits 96-127 of a[i] 
mov eax, a0 
mov ebx, a1 
mov ecx, a2 
mov ecx, a3 

shr eax, 1 
rcr ebx, 1 
rcr ecx, 1 
rcr edx, 1 

; b0 - bits 0-31 of b[i] := a[i] >> 1 
; b1 - bits 32-63 of b[i] := a[i] >> 1 
; b2 - bits 64-95 of b[i] := a[i] >> 1 
; b3 - bits 96-127 of b[i] := a[i] >> 1 
mov b0, eax 
mov b1, ebx 
mov b2, ecx 
mov b3, edx 

shr eax, 1 
rcr ebx, 1 
rcr ecx, 1 
rcr edx, 1 

; c0 - bits 0-31 of c[i] := a[i] >> 2 = b[i] >> 1 
; c1 - bits 32-63 of c[i] := a[i] >> 2 = b[i] >> 1 
; c2 - bits 64-95 of c[i] := a[i] >> 2 = b[i] >> 1 
; c3 - bits 96-127 of c[i] := a[i] >> 2 = b[i] >> 1 
mov c0, eax 
mov c1, ebx 
mov c2, ecx 
mov c3, edx 

Jeśli twój cel jest x86-64 to upraszcza się do:

; a0 - bits 0-63 of a[i] 
; a1 - bits 64-127 of a[i] 
mov rax, a0 
mov rbx, a1 

shr rax, 1 
rcr rbx, 1 

; b0 - bits 0-63 of b[i] := a[i] >> 1 
; b1 - bits 64-127 of b[i] := a[i] >> 1 
mov b0, rax 
mov b1, rbx 

shr rax, 1 
rcr rbx, 1 

; c0 - bits 0-63 of c[i] := a[i] >> 2 = b[i] >> 1 
; c1 - bits 64-127 of c[i] := a[i] >> 2 = b[i] >> 1 
mov c0, rax 
mov c1, rbx 

UPDATE: poprawione literówki w wersji 64-bitowej

+0

Niestety instrukcje RCR/RCL są wyjątkowo powolne na prawie wszystkich współczesnych procesorach.SHLD/SHRD to lepsza alternatywa. – hirschhornsalz

+0

W drugim przypadku zamiast ** shr eax, 1; rcr ebx, 1 ** musi być ** shr rax, 1; rcr rbx, 1 ** –

+0

RCR/RCL jest szybki, gdy drugi argument wynosi 1. Dokładnie tak jest w przypadku tego problemu. Gdy drugi argument to 1 RCR/RCL jest szybszy niż SHLD/SHRD we wszystkich współczesnych procesorach: –

Powiązane problemy