こういう波形見ると頭がバグってきますよね。
ちまちまと自作RISC-Vコアを実装している。DhrystoneがPASSできるようになったが、まだまだ性能的にはひどいもんだ。
特に分岐予測器を入れていないのがひどい。現在簡単なBTBとBimodal Predictorを実装しているが、適当に実装したところ思ったよりも性能が上がらなかった。 何が起きているのか見てみるために、もう少し詳細なログを取ってみる。
以下はその一端。比較分岐命令を57回実行して30回しかヒットしなかったって。プププ。(それよりもUnconditionalをノーケアなので全く予測できていないのがひどい)。
" 20386" : { "commit" : { "cmt" : 108, "inst" : 349, "dead" : 225 }, "icache" : { "request" : 439, "hit" : 409, "miss" : 30 }, "dcache" : { "port[0]" : { "req" : 0, "hit" : 0, "miss" : 0, "conflict" : 0 } "port[1]" : { "req" : 0, "hit" : 0, "miss" : 0, "conflict" : 0 } "port[2]" : { "req" : 0, "hit" : 0, "miss" : 0, "conflict" : 0 } "port[3]" : { "req" : 43, "hit" : 32, "miss" : 8, "conflict" : 3 } "port[4]" : { "req" : 82, "hit" : 68, "miss" : 5, "conflict" : 9 } "port[5]" : { "req" : 63, "hit" : 45, "miss" : 1, "conflict" : 17 } }, "branch" : { "execute" : 138, "cmp" : { "execute" : 57, "hit" : 30 }, "uncond" : { "execute" : 81, "hit" : 0 }, }, }
やはり分岐命令が実行されたとき、その命令の場所、命令の種類、分岐結果(Taken / NotTaken)、そして予測の結果を一覧で表示すると分かりやすいだろう。早速やってみる。 分岐命令のパイプラインにロガーを突っ込んでひたすらファイルにダンプしていく。以下はそのコード。
integer bim_fp; initial begin bim_fp = $fopen("bru_detail.log", "w"); end always_ff @ (negedge i_clk, negedge i_reset_n) begin if (i_reset_n) begin if (ex3_br_upd_if.update) begin if (r_ex3_pipe_ctrl.op != OP__) begin $fwrite(bim_fp, "%t : pc_vaddr = %08x, target_addr = %08x, %s, bim=%1d, %s, DASM(0x%08x)\n", $time, r_ex3_issue.pc_addr, ex3_br_upd_if.target_vaddr, r_ex3_result ? "Taken " : "NotTaken", ex3_br_upd_if.bim_value, ex3_br_upd_if.mispredict ? "Miss" : "Succ", r_ex3_issue.inst); end end end end // always_ff @ (negedge i_clk, negedge i_reset_n) final begin $fclose(bim_fp); end
その結果、下記のようなログが撮れるようになった。命令の表記部分はspike-dasm
で加工してある。
13362 : pc_vaddr = 00800027d8, target_addr = 00800027da, NotTaken, bim=0, Succ, beqz a5, pc + 20 13370 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=0, Miss, bne a2, a5, pc - 6 13374 : pc_vaddr = 00800027fe, target_addr = 0080002802, NotTaken, bim=0, Succ, bgeu a0, a2, pc - 20 13406 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=1, Miss, bne a2, a5, pc - 6 13450 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=2, Succ, bne a2, a5, pc - 6 13454 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=2, Succ, bne a2, a5, pc - 6 13462 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=2, Succ, bne a2, a5, pc - 6 13470 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=2, Succ, bne a2, a5, pc - 6 13478 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=2, Succ, bne a2, a5, pc - 6 13486 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=2, Succ, bne a2, a5, pc - 6 13494 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=3, Succ, bne a2, a5, pc - 6 13502 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=3, Succ, bne a2, a5, pc - 6 13510 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken , bim=3, Succ, bne a2, a5, pc - 6
最初の単純なループは問題なく予測できているようだ。ではどこで大幅な性能低下となっているのか。問題となるのは以下の部分だ。
17750 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken , bim=1, Miss, beq a5, a4, pc - 14 17818 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=1, Succ, beqz a5, pc + 14 17830 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken , bim=1, Miss, beq a5, a4, pc - 14 17902 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=2, Miss, beqz a5, pc + 14 17910 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=2, Miss, beqz a5, pc + 14 17962 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken , bim=2, Succ, beq a5, a4, pc - 14 17982 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=1, Succ, beqz a5, pc + 14 17994 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken , bim=1, Miss, beq a5, a4, pc - 14 18070 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=0, Succ, beqz a5, pc + 14 18082 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken , bim=0, Miss, beq a5, a4, pc - 14
うえ、0x008000290e
の分岐命令が全く学習されていない。そしてこれは「学習していない」のではなく、「0x008000290c
の情報更新に引っ張られてしまい正しく学習結果を取得できていない」というのが正しい気がしてきた。
つまり、同じキャッシュライン上で2種類の分岐命令が存在している場合、現在は最初の分岐命令の学習結果のみを抽出してそのキャッシュラインの代表として使っているが、その結果別の分岐命令の学習結果が活用されていない。
この場合、同一キャッシュライン内であっても命令毎に学習結果を取得する必要があるだろう。分岐予測器が大きくなるがやむを得ない。