つかびーの技術日記

情報系修士卒のWeb系技術日記です。現在のフォーカス分野はアドテクです。

Scalaの構造的部分型の性能に注意

      2015/03/22

今回はScalaの構造的部分型(structual subtyping)の話です。

構造的部分型を使うと、いちいちインタフェースであるtraitやclassを用意しなくて良いため、便利です。ですが、構造的部分型はコンパイルするとリフレクションに置き換えられるため、性能面では若干心配です。

今回は性能を検証してみようと思います。結論から言うと構造的部分型を利用するとメソッド呼び出しにかかるコストが41倍も増えてしまいます。(つまり沢山呼ばれる部分に使うと辛い)

構造的部分型のサンプル

まずはおさらいと例です。構造的部分型はこんな感じですね。

このように書くことで、通常インタフェース用のTraitを用意して、継承関係を考慮しつつ関数を定義して・・・ということが不要になります。printAllメソッドにはfindAll():List[_]という関数を持つオブジェクトであればなんでも与えられるようになります。

リフレクションの確認

性能の検証に入る前に、本当にリフレクションが使われているかを確かめてみます。

先ほどのソースをMain.scalaとして保存して、scalacでコンパイルしてみます。

いくつかファイルができました。.classファイルは4つできていますね。

これをjava decompilerのGUI版で読み込んでみます。Main$.classはこのようになっています。

いろいろありますが、重要なのはここで

methodオブジェクトを取ってinvokeを呼び出していますね。

確かにリフレクションになっています。

性能検証

いよいよ性能を見ていきたいと思います。

上記の例のままだと検証しづらいので検証用に以下のコードを用意しました。sbt-jmhというプラグインを使って性能測定をします。

CalcOperatorというtraitを用意して、これを経由して処理を行うnotReflectionFuncと、CalcOperator関係なしに構造的部分型で処理を行うreflectionFuncを用意しました。

これによって片方はリフレクション無しで、片方はリフレクションでaddを呼び出すことになります。

sbt-jmhでベンチマークを行います。

結果

いろいろ出力されますが、最後に以下のようにサマリーがでます。

数値が高い方が速いため、リフレクション無しversionの方が圧倒的に早いことが分かりました。5:205なので、リフレクション無しversionの方が41倍早いです。

まとめ

構造的部分型はリフレクションとして扱われるため、メソッド呼び出しは相当遅くなります。上記の通り41倍という開きがあります。

構造的部分型の利用には注意しましょう。

 

おまけ

なるほど、そんな使い道もあるのか・・・

 

 - Scala , , ,