Meilleur cas 8 cycles, pire cas 12 cycles
Comme ce n'est pas clair dans la question, je fonde cela sur les latences d'Ivy Bridge.
L'approche ici consiste à utiliser l' bsrinstruction (bit scan reverse) comme log2 () du pauvre. Le résultat est utilisé comme index dans une table de sauts qui contient des entrées pour les bits 0 à 42. Je suppose que, étant donné que l'opération sur les données 64 bits est implicitement requise, alors l'utilisation de l' bsrinstruction est OK.
Dans le meilleur des cas, l'entrée jumptable peut mapper le bsrrésultat directement à l'amplitude. Par exemple, pour les entrées dans la plage 32-63, le bsrrésultat sera 5, qui est mappé à une amplitude de 1. Dans ce cas, le chemin d'instruction est:
Instruction Latency
bsrq 3
jmp 2
movl 1
jmp 2
total 8
Dans le pire des cas, les entrées bsrseront mappées à deux amplitudes possibles, de sorte que l'entrée de saut peut en faire une supplémentaire cmppour vérifier si l'entrée est> 10 n . Par exemple, pour les entrées dans la plage 64-127, le bsrrésultat sera 6. L'entrée de pontage correspondante vérifie ensuite si l'entrée> 100 et définit la grandeur de sortie en conséquence.
De plus, pour le pire des cas, nous avons une instruction mov supplémentaire pour charger une valeur immédiate de 64 bits à utiliser dans le cmp, donc le pire des instructions est:
Instruction Latency
bsrq 3
jmp 2
movabsq 1
cmpq 1
ja 2
movl 1
jmp 2
total 12
Voici le 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 */
Cela a été principalement généré à partir de la sortie de l'assembleur gcc pour le code C de preuve de concept que j'ai écrit . Notez que le code C utilise un goto calculable pour implémenter la table de saut. Il utilise également la fonction __builtin_clzll()intégrée gcc, qui compile l' bsrinstruction (plus un xor).
J'ai envisagé plusieurs solutions avant d'arriver à celle-ci:
FYL2Xpour calculer le logarithme naturel, puis FMULpar la constante nécessaire. Ce serait probablement gagner si c'était un concours [tag: instruction: golf]. Mais FYL2Xa une latence de 90-106 pour Ivy bridge.
Recherche binaire codée en dur. Cela peut en fait être compétitif - je laisse le soin à quelqu'un d'autre de l'implémenter :).
Tableau de recherche complet des résultats. Je suis sûr que cela est théoriquement le plus rapide, mais nécessiterait une table de recherche de 1 To - pas encore pratique - peut-être dans quelques années si la loi de Moore continue de tenir.