ベストケース8サイクル、ワーストケース12サイクル
質問では明確ではないので、これはIvy Bridgeの待ち時間に基づいています。
ここでのアプローチはbsr
、貧乏人のlog2()として(ビットスキャンリバース)命令を使用することです。結果は、ビット0〜42のエントリを含むジャンプテーブルへのインデックスとして使用されます。64ビットデータに対する操作が暗黙的に必要であると仮定すると、bsr
命令の使用はOKです。
最良の場合の入力では、ジャンプテーブルエントリはbsr
結果を絶対値に直接マッピングできます。たとえば、範囲が32〜63の入力の場合、bsr
結果は5になり、これは大きさ1にマッピングされます。この場合、命令パスは次のとおりです。
Instruction Latency
bsrq 3
jmp 2
movl 1
jmp 2
total 8
最悪の場合、bsr
結果は2つの可能な大きさにマッピングされるため、ジャンプテーブルエントリはcmp
入力が10 nを超えるかどうかを確認するために1回追加します。たとえば、範囲64〜127の入力の場合、bsr
結果は6になります。対応するジャンプテーブルエントリは、入力が100より大きいかどうかを確認し、それに応じて出力の大きさを設定します。
最悪の場合のパスに加えて、で使用する64ビットの即値をロードするmov命令が追加されているcmp
ため、最悪の場合の命令パスは次のようになります。
Instruction Latency
bsrq 3
jmp 2
movabsq 1
cmpq 1
ja 2
movl 1
jmp 2
total 12
コードは次のとおりです。
/* 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 */
これは、私が書いた概念実証Cコードの gccアセンブラー出力からほとんど生成されました。Cコードでは、計算可能なgotoを使用してジャンプテーブルを実装しています。また__builtin_clzll()
、bsr
命令にコンパイルされるgccビルトインも使用します(プラスxor
)。
これに到達する前に、いくつかの解決策を検討しました。
FYL2X
自然対数を計算するにはFMUL
、必要な定数を使用します。[tag:instruction:golf]コンテストであれば、おそらくこれが勝つでしょう。ただしFYL2X
、Ivyブリッジでは90〜106のレイテンシがあります。
ハードコードされたバイナリ検索。これは実際に競争力があるかもしれません-私はそれを実装するために他の誰かに任せます:)。
結果の完全なルックアップテーブル。これは理論的には最速だと確信していますが、ムーアの法則が引き続き適用される場合は、おそらく数年のうちに1TBのルックアップテーブルが必要になります(まだ実用的ではありません)。