En iyi durum 8 döngü, En kötü durum 12 döngü
Soruda net olmadığı için bunu Ivy Bridge gecikmelerinden dayandırıyorum.
Buradaki yaklaşım bsr(bit taraması ters) komutunu fakir bir adamın log2 () olarak kullanmaktır. Sonuç, 0 ila 42 bitleri içeren girişleri içeren bir atlama tablosuna bir dizin olarak kullanılır. 64bit veriler üzerinde işlemin dolaylı olarak gerekli olduğu göz önüne alındığında, bsrkomutun kullanımının iyi olduğunu varsayıyorum .
En iyi durum girişlerinde, atlanabilir tablo girişi bsrsonucu doğrudan büyüklüğe eşleyebilir . Örneğin, 32-63 aralığındaki girişler için, bsrsonuç 1 büyüklüğünde olacak şekilde 5 olacaktır. Bu durumda, komut yolu:
Instruction Latency
bsrq 3
jmp 2
movl 1
jmp 2
total 8
En kötü durum girişlerinde, bsrsonuç iki olası büyüklükle eşleşecektir, bu nedenle atlanabilir giriş cmp, girişin> 10 n olup olmadığını kontrol etmek için bir ek daha yapar . Örneğin bsr64-127 aralığındaki girişler için sonuç 6 olur. Karşılık gelen takviye edilebilir giriş daha sonra girişin> 100 olup olmadığını kontrol eder ve çıkış büyüklüğünü buna göre ayarlar.
En kötü durum yoluna ek olarak, kullanımda 64bit anlık bir değer yüklemek için ek bir mov komutumuz var cmp, bu nedenle en kötü durum talimatı yolu:
Instruction Latency
bsrq 3
jmp 2
movabsq 1
cmpq 1
ja 2
movl 1
jmp 2
total 12
İşte 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 */
Bu çoğunlukla yazdığım kavram kanıtı C kodu için gcc birleştirici çıktısından üretildi . C kodu atlama tablosunu uygulamak için hesaplanabilir bir goto kullandığını unutmayın. Ayrıca __builtin_clzll(), bsrtalimatı derleyen gcc yerleşimini kullanır (artı bir xor).
Ben bu bir gelmeden önce birkaç çözüm düşündüm:
FYL2Xdoğal kütüğü hesaplamak, sonra FMULgerekli sabit ile. Bu bir [tag: instruction: golf] yarışması olsaydı bu muhtemelen kazanırdı. Ancak FYL2XIvy köprüsü için 90-106 gecikme süresi var.
Sabit kodlu ikili arama. Bu gerçekten rekabetçi olabilir - uygulamak için başka birine bırakacağım :).
Sonuçların tam arama tablosu. Bu teorik olarak en hızlı olduğundan eminim, ancak 1TB arama tablosu gerektirir - henüz pratik değil - belki birkaç yıl içinde Moore Yasası tutmaya devam ederse.