RISC-V用のgccでコードを生成して比較する
ソースコード
重力多体問題の加速度を計算する少し複雑なコード。 OSなしで直接実行するため、libm等を使うのは面倒な気がする。 平方根命令(fsqrt.s)を出力するために、インラインで命令を書き込んでおく。 なお、RV32Fには逆数平方根命令はない。
コンパイル方法
ABIを指定して浮動小数点レジスタを値渡しに使うようにする。 ABIを"ilp32f"としない場合、関数の引数や返り値には整数レジスタ(a0 - a7)を使うため、 無駄なfmv.x.w/fmv.w.x命令が生成される。
~/x-tools/riscv32-unknown-elf/bin/riscv32-unknown-elf-gcc -march=rv32imf -mabi=ilp32f -S sum.c -o sum.s
実行した場合のサイクル数比較
最適化オプションを変えてコード生成した上で、シミュレータで実行にかかったサイクル数を計測した。 生成された命令数もカウントした。生成された命令数には別途リンクするstartup用のコード(8命令)を含む。
nが小さいと内側のループがアンローリングされてコードが変化するため、 n = 15でコード生成し実行した場合のサイクル数とCPIの近似値は以下のようになった。
opt | 命令数 | サイクル数 | ループあたり | CPI |
---|---|---|---|---|
-O0 | 792 | 40668 | 180.7 | 1.6 |
-O3 | 348 | 12307 | 54.4 | 2.2 |
最適化オプションなし/"-O0"の場合
配列アクセスはその都度アドレスをロードしている。 32ビットのアドレス即値をロードするには2命令かかるので効率が悪い。 再内側ループ(49 - 151行)は114命令(関数sqrtf呼び出し含めて)。
“-O3"の場合
最初に配列のアドレスを一斉にロード。 ループ内のコードはかなりシンプルになり、再内側ループ(47 - 76行)は25命令。