Bester Fall 8 Zyklen, schlechtester Fall 12 Zyklen
Da es in der Frage nicht klar ist, begründe ich dies mit den Ivy Bridge-Latenzen.
Der Ansatz besteht hier darin, die bsr
Anweisung (Bit-Scan-Umkehrung) als log2 () eines armen Mannes zu verwenden. Das Ergebnis wird als Index für eine Sprungtabelle verwendet, die Einträge für die Bits 0 bis 42 enthält. Ich gehe davon aus, dass die Verwendung des bsr
Befehls OK ist , wenn die Operation für 64-Bit-Daten implizit erforderlich ist.
Im besten Fall kann der Eintrag jumptable die bsr
Ergebnis direkt auf die Größe . Beispiel: Für Eingaben im Bereich 32-63 ist das bsr
Ergebnis 5, was einer Größe von 1 zugeordnet wird. In diesem Fall lautet der Befehlspfad:
Instruction Latency
bsrq 3
jmp 2
movl 1
jmp 2
total 8
Im schlimmsten Fall wird das bsr
Ergebnis auf zwei mögliche Größen abgebildet, sodass der Eintrag für die Sprungtabelle eine weitere machtcmp
, um zu überprüfen, ob die Eingabe> 10 n ist . ZB für Eingaben im Bereich 64-127 ist das bsr
Ergebnis 6. Der entsprechende Sprungtabelleneintrag prüft dann, ob die Eingabe> 100 ist, und stellt die Ausgabegröße entsprechend ein.
Zusätzlich zum Worst-Case-Pfad haben wir einen zusätzlichen mov-Befehl, um einen 64-Bit-Direktwert für die Verwendung in zu laden. cmp
Der Worst-Case-Befehlspfad lautet also:
Instruction Latency
bsrq 3
jmp 2
movabsq 1
cmpq 1
ja 2
movl 1
jmp 2
total 12
Hier ist der Code:
/* 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 */
Dies wurde hauptsächlich aus der gcc-Assembler-Ausgabe für den von mir geschriebenen Proof-of-Concept-C-Code generiert . Beachten Sie, dass der C-Code ein berechenbares goto verwendet, um die Sprungtabelle zu implementieren. Es verwendet auch die__builtin_clzll()
gcc verwendet, das zur bsr
Anweisung kompiliert wird (plus ein xor
).
Ich habe mehrere Lösungen in Betracht gezogen, bevor ich zu dieser gekommen bin:
FYL2X
um dann den natürlichen log zu berechnen FMUL
durch die notwendige Konstante. Dies würde vermutlich gewinnen, wenn es ein [tag: instruction: golf] -Wettbewerb wäre. FYL2X
Hat aber so eine Latenz von 90-106 für Ivy Bridge.
Hardcodierte binäre Suche. Dies kann tatsächlich wettbewerbsfähig sein - ich überlasse es jemand anderem, dies umzusetzen :).
Komplette Nachschlagetabelle der Ergebnisse. Ich bin sicher, dass dies theoretisch am schnellsten ist, würde aber eine 1-TB-Nachschlagetabelle erfordern - noch nicht praktisch -, möglicherweise in ein paar Jahren, wenn Moores Gesetz weiterhin gilt.