FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

自作Binary Translation型RISC-VエミュレータのTCG最適化解析

前回の続き。自作Binary Translation型RISC-Vエミュレータの最適化作業、いくつか改善を取り入れたのでflamegraphを用いて最適化の結果を解析する。現状の実装でflamegraphを適用した結果が以下となった。

f:id:msyksphinz:20201208014035p:plain

これをよく見てみると、clone()が多く出てきていることが分かる。これはもしかしてCloneトレイトにより大量にオブジェクトのコピーが発生してしまっているということか?確かに、TCGの生成においてはかなり大量にCopyやらCloneを使ってしまっている。これを削減する必要があるということだろうか。

試しに、これまですべてのインスタンスをCopyで繋げていたものを、ポインタで繋げ直すとどのようになるのだろう?Rustでポインタを扱うのが非常に面倒なので、Rc<RefCell<>>を使って一気に書き直してみることにした。 例えば、今までこのように実装していたものが、

    pub fn translate_rrr(&mut self, op: TCGOpcode, inst: &InstrInfo) -> Vec<TCGOp> {
        let rs1_addr= get_rs1_addr!(inst.inst);
        let rs2_addr= get_rs2_addr!(inst.inst);
        let rd_addr = get_rd_addr!(inst.inst);

        if rd_addr == 0 {
            return vec![];
        }

        let source1 = self.tcg_temp_new();
        let source2 = self.tcg_temp_new();

        let rs1_op = TCGOp::tcg_get_gpr(source1, rs1_addr);
        let rs2_op = TCGOp::tcg_get_gpr(source2, rs2_addr);  // Box::new(TCGv::new_reg(rs2_addr as u64));

        let tcg_inst = TCGOp::new_3op(op, source1, source1, source2);

        let rd_op = TCGOp::tcg_set_gpr(rd_addr, source1);

        self.tcg_temp_free(source2);
        self.tcg_temp_free(source1);

        vec![rs1_op, rs2_op, tcg_inst, rd_op]
    }

以下のように変更する。もう大量のRc<RefCell<>>である。疲れた...

    pub fn translate_rrr(&mut self, op: TCGOpcode, inst: &InstrInfo) -> Vec<TCGOp> {
        let rs1_addr= get_rs1_addr!(inst.inst);
        let rs2_addr= get_rs2_addr!(inst.inst);
        let rd_addr = get_rd_addr!(inst.inst);

        if rd_addr == 0 {
            return vec![];
        }

        let source1 = self.tcg_temp_new();
        let source2 = self.tcg_temp_new();

        let rs1_op = TCGOp::tcg_get_gpr(Rc::clone(&source1), rs1_addr);
        let rs2_op = TCGOp::tcg_get_gpr(Rc::clone(&source2), rs2_addr);  // Box::new(TCGv::new_reg(rs2_addr as u64));

        let tcg_inst = TCGOp::new_3op(op, Rc::clone(&source1),  Rc::clone(&source1),  Rc::clone(&source2));

        let rd_op = TCGOp::tcg_set_gpr(rd_addr, Rc::clone(&source1));

        self.tcg_temp_free(Rc::clone(&source2));
        self.tcg_temp_free(Rc::clone(&source1));

        vec![rs1_op, rs2_op, tcg_inst, rd_op]
    }

このように変更した結果、パタン自体は正しく動作するが、若干速度が低下した。つまり、インスタンスをCopyするかどうかというのはあまり速度に影響しなかったということなのか? flamegraphを取ると以下のようになった。もう、run()の以下が全く解析できず、どこに律速されているのか分からなくなっている...

f:id:msyksphinz:20201208014735p:plain

明確なボトルネックになる部分が残っているとは思えないので、やはり少しずつ時間を計測しながら解析するしかないか...