2016-09-14 15 views
4

私はScalaを初めて使い慣れました。私が他の人が書いたScalaコードを読んで勉強しているとき、Scalaのコードで他の言語と違う点はパターンマッチングです。Scalaでのパターンマッチングの速さ

同時に私はそれがもたらす利便性と表現力を感じます。その背後にある潜在的なパフォーマンスコストが不思議であるのを助けることはできません - 一般的に言えば、どのくらい速いですかmatch

まず、コンストラクタの一致するパラメータなどの「高度な」機能がない場合、IMOのScalaのmatchは、switch-caseの対応言語です。例えば、初心者として

color match { 
    0 => println "color is red!" 
    1 => println "color is green!" 
    2 => println "color is blue!" 
} 

は、私は上記のコードはif-else文で同等のコードとまったく同じ速さであるかどうかを知りたいですか?今それらを取って第二

、「高度な」例えば、バック機能:

は試合の他の機能(リストマッチング、ペアマッチングなど)または上記のコードについては
expr match { 
    Animal(name) => ... 
    Animal(name, age) => ... 
    Animal(_, _, id) => ... 
} 

、私は好奇心ですScalaがこのような使い方をどのように実装したか?そして、最も重要なことは、これらのコードがどれくらい速くなると思いますか? (言って、最初のケースではまだmatchと同じくらい速いですか、少し遅くなったり、反射などの技術を使用して非常に遅いですか?)

ありがとうございます!

+1

は、文脈に依存します:isInstanceOf(無反射または類似のもの)を介して手動チェックなどのパフォーマンスを(コンパイラが何かを最適化することができた場合、またはより良い)しかし、それは同じ持っている定数を含む限られた場合には、それは次のようになりますjavaスイッチ。組み込みのcaseクラスパターンでパターンマッチングが必要な場合は、単にinstanceofのチェックを行い、次にフィールドのメンバー検索を行います。一般的な場合、完全な 'unapply'メソッドが呼び出されます(そして、それはより遅いです)。 – Alec

答えて

7

まずスニペットは、バイトコードのTableSwitch(またはLookupSwitch)に変換し、Javaのスイッチ/ケースほど高速です:

セカンドスニップはunapply/isInstanceOf/null checks呼び出しの束に変換し、遅くtableswitchをより(明らかに)です

scala> def foo(i: Int) = i match { 
    | case 1 => 2 
    | case 2 => 10 
    | case 3 => 42 
    | case _ => 777 
    | } 
foo: (i: Int)Int 

scala> :javap -c foo 
Compiled from "<console>" 
public class { 
    public static final MODULE$; 

    public static {}; 
    Code: 
     0: new   #2     // class 
     3: invokespecial #12     // Method "<init>":()V 
     6: return 

    public int foo(int); 
    Code: 
     0: iload_1 
     1: istore_2 
     2: iload_2 
     3: tableswitch { // 1 to 3 
        1: 44 
        2: 39 
        3: 34 
       default: 28 
      } 
     28: sipush  777 
     31: goto   45 
     34: bipush  42 
     36: goto   45 
     39: bipush  10 
     41: goto   45 
     44: iconst_2 
     45: ireturn 

    public(); 
    Code: 
     0: aload_0 
     1: invokespecial #18     // Method java/lang/Object."<init>":()V 
     4: aload_0 
     5: putstatic  #20     // Field MODULE$:L; 
     8: return 

scala> case class Foo(s: String, i: Int) 
defined class Foo 

scala> def bar(foo: Foo) = foo match { 
    | case Foo("test", _) => 1 
    | case Foo(_, 42) => 2 
    | case _ => 3 
    | } 
bar: (foo: Foo)Int 

scala> :javap -c bar 
Compiled from "<console>" 
public class { 
    public static final MODULE$; 

    public static {}; 
    Code: 
     0: new   #2     // class 
     3: invokespecial #12     // Method "<init>":()V 
     6: return 

    public int bar(Foo); 
    Code: 
     0: aload_1 
     1: astore_2 
     2: aload_2 
     3: ifnull  26 
     6: aload_2 
     7: invokevirtual #20     // Method Foo.s:()Ljava/lang/String; 
     10: astore_3 
     11: ldc   #22     // String test 
     13: aload_3 
     14: invokevirtual #26     // Method java/lang/Object.equals:(Ljava/lang/Object;)Z 
     17: ifeq   26 
     20: iconst_1 
     21: istore  4 
     23: goto   52 
     26: aload_2 
     27: ifnull  49 
     30: aload_2 
     31: invokevirtual #30     // Method Foo.i:()I 
     34: istore  5 
     36: bipush  42 
     38: iload   5 
     40: if_icmpne  49 
     43: iconst_2 
     44: istore  4 
     46: goto   52 
     49: iconst_3 
     50: istore  4 
     52: iload   4 
     54: ireturn 

    public(); 
    Code: 
     0: aload_0 
     1: invokespecial #34     // Method java/lang/Object."<init>":()V 
     4: aload_0 
     5: putstatic  #36     // Field MODULE$:L; 
     8: return 
} 
+0

独自の特別な扱いをしているので、caseコンストラクタのパターンにマッチするパターンを見るのはあまりお粗末ではありません。独自の 'unapply'パターンを書く場合、' javap'ダンプはもっと醜いと思います。 – Alec

+1

さて、あまりにも醜いです。これは、2番目のバイトコードスニペット( 'isInstanceOf'、ヌルチェック)に似ていますが、可能な最適化はありません。 Scalaコンパイラは、ケースクラスのメカニズムが固定されているのに対し、エクストラクタ内のunapplyメソッドまたはunapplySeqメソッドは、ほとんどすべての処理を行うことができるため、エクストラクタよりもはるかに優れたケースクラスのパターンを最適化できます。 – dveim

+0

私は、 'option [(..)]オプションタプルオブジェクトの少なくともいくつかは非最適化されていると推測していたでしょう(パターンはインスタンス化とヌルチェックだけを必要とするコンストラクタパターンとは異なり、それは比較的非効率的だと思われる。 – Alec

関連する問題