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, bsr
komutun kullanımının iyi olduğunu varsayıyorum .
En iyi durum girişlerinde, atlanabilir tablo girişi bsr
sonucu doğrudan büyüklüğe eşleyebilir . Örneğin, 32-63 aralığındaki girişler için, bsr
sonuç 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, bsr
sonuç 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 bsr
64-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()
, bsr
talimatı derleyen gcc yerleşimini kullanır (artı bir xor
).
Ben bu bir gelmeden önce birkaç çözüm düşündüm:
FYL2X
doğal kütüğü hesaplamak, sonra FMUL
gerekli sabit ile. Bu bir [tag: instruction: golf] yarışması olsaydı bu muhtemelen kazanırdı. Ancak FYL2X
Ivy 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.