Re: そのコード、本当に最適化されてますか?

Nagai Masato 氏の Exploring Groovy 1.8 Part 1 - そのコード、本当に最適化されてますか? を読んでコメントしようとしたができなかったので。

if-else と if + return は変わらない

エントリのソースを試していて偶然気が付いた。{} をつければ変わらない。

// 最適化されない
int fib1(int i) {
  if (i < 2)
    1
  else
    fib1(i - 2) + fib1(i - 1)
}

// 最適化される
int fib2(int i) {
  if (i < 2) {
    1
  } else
    fib2(i - 2) + fib2(i - 1)
}

理由はわからない*1
気づいたのは fib1 を1行で書くと Groovy1.7 では parse error だから
三項演算子の方は私の環境ではこの 1.3 倍ぐらいだった。


groovyc して decompile してみると

// 最適化を適用するかの判定処理がある
if(!BytecodeInterface8.isOrigInt() || __$stMC || BytecodeInterface8.disabledStandardMetaClass())
  // 今回は関係ないのでパス
  ...
if(i < 2)
  return 1;
else
  return DefaultTypeTransformation.intUnbox(
    acallsite[13].call(
      acallsite[14].callCurrent(this, (Integer)DefaultTypeTransformation.box(i - 2)),
      acallsite[15].callCurrent(this, (Integer)DefaultTypeTransformation.box(i - 1))));


一番早い場合は Java と同じ

// 判定処理は同じ
...
if(i < 2)
  return 1;
else
  return fib2(i - 2) + fib2(i - 1);


三項演算子は戻り値が wrapper 型の分、3割遅い?

return DefaultTypeTransformation.intUnbox(
  (i >= 2) ? ((Object) ((Integer)DefaultTypeTransformation.box(fib(i - 2) + fib(i - 1)))) :
             ((Object) ((Integer)DefaultTypeTransformation.box(1))));


1番上は三項演算子より 5 倍近く遅かったので wrapper の問題で遅いのではなくメソッド呼び出しの関係で遅いということになる。
でも Groovy MOP などは call のお陰で実現できているので MOP できなくなるぐらいなら今のままでいいけど。

for と foreach

for は decompile がうまく行かなかったので何が理由で遅いのか調べていない。
Groovy - Wikipedia に書かれているので知っている人も多いと思うけど foreach はもともと for より少し速い。
これは from と to が int の場合は int に特化した Range を返しているから。
他の型 'a'..'z' とかだとほぼ for と同じになった。
これも call が遅い理由であれば現時点では for には工夫の余地がありそう。


BytecodeInterface8 をみる*2と実装されていないフラグがいろいろあるので、まだ記述を気にするような段階でない気がする。


最後になりましたが Nagai Masato 様へ startGroovy.bat ありがたく使わせていただきます。

*1:最後の if の return は以前はできなかったのでそのあたりの処理?

*2:1.8 の 8 ?