LoginSignup
4
3

More than 5 years have passed since last update.

CrystalのIntのパフォーマンスが悪くなかったのが予想外だったのでLLVM中間表現を確認した

Posted at

背景

前回の記事でCrystalのベンチマークを行った。
http://qiita.com/irxground/items/cd7368444caa5e27ecdd

もちろんこれは「悪くなるだろう」と予想したから計測したのだが、全然そんなことはなかったので、実際に「なぜ悪くならなかったか」を調べてみた。

最近の機械語を吐く言語はたいてい、LLVMを利用しているので、LLVMの中間表現を読んでみるのが手っ取り早い。

結果

Crystalは引数の型が Int8 | Int16 | Int32 | Int64 のようなUnionだった場合、引数の型ごとに関数を生成するようだ。確かにこの方法なら実行時のパフォーマンス劣化は起こらない。
しかも実際に利用した型のみ生成されているので(つまり今回で言えば、Int8Int16の分の関数は生成されない)安心して利用できる。

コード等

コード

前回の一部を利用

fib_example.cr
def fib_32(n : Int32)
  if n < 2
    n
  else
    fib_32(n - 1) + fib_32(n - 2)
  end
end

def fib_signed(n : Int::Signed)
  if n < 2
    n
  else
    fib_signed(n - 1) + fib_signed(n - 2)
  end
end

N = 20
pp fib_32(N)
pp fib_signed(N.to_i32)
pp fib_signed(N.to_i64)

生成

$ crystal build --emit llvm-ir --release fib_example.cr

生成されたLLVM中間表現

自分の手元だと中間表現は 12,715行もの巨大なコードになるので、ぱっと見圧倒されるが、LLVM中間表現上の関数名には元のCrystalの関数名が含まれるので、関数名で検索すれば簡単に見つかる。

ただし今回の例ではうまくいっているが(CrystalがやっているのかLLVMがやっているのかわからないが)関数のインライン展開はかなり行われるので、もし同じようにLLVM中間表現を読もうとしている人は注意したほうがいいと思う。

; (略)

; Function Attrs: nounwind readnone uwtable
define internal fastcc i32 @"*fib_32<Int32>:Int32"(i32 %n) #5 {
entry:
  %0 = icmp slt i32 %n, 2
  br i1 %0, label %exit, label %else

else:                                             ; preds = %entry
  %1 = add i32 %n, -1
  %2 = tail call fastcc i32 @"*fib_32<Int32>:Int32"(i32 %1)
  %3 = add i32 %n, -2
  %4 = tail call fastcc i32 @"*fib_32<Int32>:Int32"(i32 %3)
  %5 = add i32 %4, %2
  ret i32 %5

exit:                                             ; preds = %entry
  ret i32 %n
}

; (略)

; Function Attrs: nounwind readnone uwtable
define internal fastcc i32 @"*fib_signed<Int32>:Int32"(i32 %n) #5 {
entry:
  %0 = icmp slt i32 %n, 2
  br i1 %0, label %exit, label %else

else:                                             ; preds = %entry
  %1 = add i32 %n, -1
  %2 = tail call fastcc i32 @"*fib_signed<Int32>:Int32"(i32 %1)
  %3 = add i32 %n, -2
  %4 = tail call fastcc i32 @"*fib_signed<Int32>:Int32"(i32 %3)
  %5 = add i32 %4, %2
  ret i32 %5

exit:                                             ; preds = %entry
  ret i32 %n
}

; Function Attrs: nounwind readnone uwtable
define internal fastcc i64 @"*fib_signed<Int64>:Int64"(i64 %n) #5 {
entry:
  %0 = icmp slt i64 %n, 2
  br i1 %0, label %exit, label %else

else:                                             ; preds = %entry
  %1 = add i64 %n, -1
  %2 = tail call fastcc i64 @"*fib_signed<Int64>:Int64"(i64 %1)
  %3 = add i64 %n, -2
  %4 = tail call fastcc i64 @"*fib_signed<Int64>:Int64"(i64 %3)
  %5 = add i64 %4, %2
  ret i64 %5

exit:                                             ; preds = %entry
  ret i64 %n
}

; (略)

しかし、fib32fib_32<Int32>という関数名になっているのは不思議だ……

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3