Caso migliore 8 cicli, caso peggiore 12 cicli
Dal momento che non è chiaro nella domanda, lo sto basando sulle latenze di Ivy Bridge.
L'approccio qui è usare l' bsr
istruzione (bit scan reverse) come log2 () di un uomo povero. Il risultato viene utilizzato come indice in una tabella di salto che contiene voci per i bit da 0 a 42. Presumo che dato che l'operazione sui dati a 64 bit è implicitamente richiesta, l'uso bsr
dell'istruzione è OK.
Nel migliore dei casi, la voce jumptable può mappare il bsr
risultato direttamente sulla grandezza. ad es. per gli ingressi nell'intervallo 32-63, il bsr
risultato sarà 5, che è mappato su una grandezza di 1. In questo caso, il percorso dell'istruzione è:
Instruction Latency
bsrq 3
jmp 2
movl 1
jmp 2
total 8
Nei casi peggiori, il bsr
risultato verrà mappato su due possibili magnitudini, quindi la voce jumptable fa un'ulteriore cmp
per verificare se l'ingresso è> 10 n . Ad esempio, per gli ingressi nell'intervallo 64-127, il bsr
risultato sarà 6. La voce jumptable corrispondente controlla quindi se l'ingresso> 100 e imposta la grandezza di uscita di conseguenza.
Inoltre per il percorso del caso peggiore, abbiamo un'istruzione mov aggiuntiva per caricare un valore immediato a 64 bit da utilizzare in the cmp
, quindi il percorso dell'istruzione nel caso peggiore è:
Instruction Latency
bsrq 3
jmp 2
movabsq 1
cmpq 1
ja 2
movl 1
jmp 2
total 12
Ecco il codice:
/* 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 */
Ciò è stato in gran parte generato dall'output dell'assembler gcc per il codice C di prova del concetto che ho scritto . Si noti che il codice C utilizza un goto calcolabile per implementare la tabella di salto. Utilizza anche il __builtin_clzll()
comando incorporato gcc, che viene compilato per l' bsr
istruzione (più un xor
).
Ho considerato diverse soluzioni prima di arrivare a questo:
FYL2X
per calcolare il registro naturale, quindi FMUL
per la costante necessaria. Questo presumibilmente vincerebbe questo se fosse un concorso [tag: istruzioni: golf]. Ma FYL2X
ha una latenza di 90-106 per il ponte Ivy.
Ricerca binaria codificata. Questo potrebbe effettivamente essere competitivo - lo lascerò a qualcun altro da implementare :).
Tabella di ricerca completa dei risultati. Questo sono sicuro che sia teoricamente più veloce, ma richiederebbe una tabella di ricerca da 1 TB - non ancora pratica - forse tra qualche anno se la Legge di Moore continuerà a essere valida.