Najlepszy przypadek 8 cykli, Najgorszy przypadek 12 cykli
Ponieważ pytanie nie jest jasne, opieram to na opóźnieniach Ivy Bridge.
Podejście polega na użyciu instrukcji bsr(odwrotne skanowanie bitów) jako log2 biedaka (). Wynik jest używany jako indeks do tabeli skoków, która zawiera wpisy dla bitów od 0 do 42. Zakładam, że biorąc pod uwagę, że operacja na danych 64-bitowych jest niejawnie wymagana, to użycie bsrinstrukcji jest OK.
W najlepszych przypadkach dane wejściowe z możliwością zbierania mogą odwzorować bsrwynik bezpośrednio na wielkość. np. dla danych wejściowych z zakresu 32-63 bsrwynikiem będzie 5, który jest odwzorowany na wielkość 1. W tym przypadku ścieżka instrukcji to:
Instruction Latency
bsrq 3
jmp 2
movl 1
jmp 2
total 8
W najgorszym przypadku dane wejściowe bsrzostaną odwzorowane na dwie możliwe wielkości, więc wpis z możliwością zbierania dodaje jeszcze jedną wartość, cmpaby sprawdzić, czy wartość wejściowa jest> 10 n . Np. Dla danych wejściowych z zakresu 64-127 bsrwynik będzie wynosić 6. Odpowiedni wpis , który można przesuwać, sprawdza, czy dane wejściowe> 100, i odpowiednio ustawia wielkość wyjściową.
Dodatkowo dla ścieżki najgorszego przypadku mamy dodatkową instrukcję mov, aby załadować 64-bitową wartość natychmiastową do użycia w cmp, więc ścieżka instrukcji najgorszego przypadku to:
Instruction Latency
bsrq 3
jmp 2
movabsq 1
cmpq 1
ja 2
movl 1
jmp 2
total 12
Oto kod:
/* Input is loaded in %rdi */
bsrq %rdi, %rax
jmp *jumptable(,%rax,8)
.m0:
movl $0, %ecx
jmp .end
.m0_1:
cmpq $9, %rdi
ja .m1
movl $0, %ecx
jmp .end
.m1:
movl $1, %ecx
jmp .end
.m1_2:
cmpq $99, %rdi
ja .m2
movl $1, %ecx
jmp .end
.m2:
movl $2, %ecx
jmp .end
.m2_3:
cmpq $999, %rdi
ja .m3
movl $2, %ecx
jmp .end
.m3:
movl $3, %ecx
jmp .end
.m3_4:
cmpq $9999, %rdi
ja .m4
movl $3, %ecx
jmp .end
.m4:
movl $4, %ecx
jmp .end
.m4_5:
cmpq $99999, %rdi
ja .m5
movl $4, %ecx
jmp .end
.m5:
movl $5, %ecx
jmp .end
.m5_6:
cmpq $999999, %rdi
ja .m6
movl $5, %ecx
jmp .end
.m6:
movl $6, %ecx
jmp .end
.m6_7:
cmpq $9999999, %rdi
ja .m7
movl $6, %ecx
jmp .end
.m7:
movl $7, %ecx
jmp .end
.m7_8:
cmpq $99999999, %rdi
ja .m8
movl $7, %ecx
jmp .end
.m8:
movl $8, %ecx
jmp .end
.m8_9:
cmpq $999999999, %rdi
ja .m9
movl $8, %ecx
jmp .end
.m9:
movl $9, %ecx
jmp .end
.m9_10:
movabsq $9999999999, %rax
cmpq %rax, %rdi
ja .m10
movl $9, %ecx
jmp .end
.m10:
movl $10, %ecx
jmp .end
.m10_11:
movabsq $99999999999, %rax
cmpq %rax, %rdi
ja .m11
movl $10, %ecx
jmp .end
.m11:
movl $11, %ecx
jmp .end
.m11_12:
movabsq $999999999999, %rax
cmpq %rax, %rdi
ja .m12
movl $11, %ecx
jmp .end
.m12:
movl $12, %ecx
jmp .end
jumptable:
.quad .m0
.quad .m0
.quad .m0
.quad .m0_1
.quad .m1
.quad .m1
.quad .m1_2
.quad .m2
.quad .m2
.quad .m2_3
.quad .m3
.quad .m3
.quad .m3
.quad .m3_4
.quad .m4
.quad .m4
.quad .m4_5
.quad .m5
.quad .m5
.quad .m5_6
.quad .m6
.quad .m6
.quad .m6
.quad .m6_7
.quad .m7
.quad .m7
.quad .m7_8
.quad .m8
.quad .m8
.quad .m8_9
.quad .m9
.quad .m9
.quad .m9
.quad .m9_10
.quad .m10
.quad .m10
.quad .m10_11
.quad .m11
.quad .m11
.quad .m11_12
.quad .m12
.quad .m12
.quad .m12
.end:
/* output is given in %ecx */
Zostało to wygenerowane głównie z danych wyjściowych asemblera gcc dla kodu C w wersji proof-of-concept, który napisałem . Zauważ, że kod C używa obliczalnego goto do implementacji tabeli skoków. Używa również __builtin_clzll()wbudowanego gcc, który kompiluje się do bsrinstrukcji (plus an xor).
Rozważałem kilka rozwiązań, zanim doszedłem do tego:
FYL2Xobliczyć logarytm naturalny, a następnie FMULprzez niezbędną stałą. Prawdopodobnie wygrałoby to, gdyby był to konkurs [tag: instrukcja: golf]. Ale FYL2Xma opóźnienie 90-106 dla mostu Ivy.
Zakodowane wyszukiwanie binarne. To może faktycznie być konkurencyjne - zostawię to komuś innemu do wdrożenia :).
Kompletna tabela wyników. Jestem pewien, że jest teoretycznie najszybszy, ale wymagałby tabeli przeglądowej 1 TB - jeszcze niepraktycznej - może za kilka lat, jeśli Prawo Moore'a będzie nadal obowiązywać.