2013-05-28 12 views
6

Scala for Comprehensionsは実際にはかなり遅いと言われています。私が与えられた理由は、Javaの制限により、渡された関数を呼び出すために、各反復で一時オブジェクトを生成する必要があるということです(後述の「reduce」など)。"for loop comprehensions"のScalaはなぜFORループに比べて非常に遅いのですか?

ISです。 .THIS ... TRUE?以下のテストではこれを補うように見えますが、なぜこれが当てはまるのか完全に理解していません。

"lambdas"または匿名関数には意味がありますが、匿名以外の関数には意味がない可能性があります。

私のテストでは、list.reduce(以下のコードを参照)に対してループを実行し、reduceに渡された関数と同じ関数が呼び出されたときでも、2倍以上速くなることがわかりました。

私はこれを非常に直感的に理解しています(Scalaライブラリはできるだけ最適になるよう注意深く作成されていたと思います)。値

  • のアレイ上

    1. forループは:私は同じループを実行し、私は一緒に入れ試験で

      、(かかわらずオーバーフローの、百万番号1を総括)は、5つの異なる方法ループのための、しかしに渡し、私は匿名関数

    2. list.reduceを渡し、付加機能を
    3. list.reduceを含むオブジェクトを作成し、forループ
    4. 代わりに、インライン算術関数を呼び出します 試験:分かるように最小/最大/平均(ミリ秒)

      1. 27/157/64.78 
      2. 27/192/65.77 <--- note the similarity between tests 1,2 and 4,5 
      3. 139/313/202.58 
      4. 63/342/150.18 
      5. 63/341/149.99 
      

      、「理解するための」バージョン」とのオーダーであり、以下のよう

    結果であったオブジェクトメンバ関数匿名と非匿名の両方の機能バージョンに対して「新しい」が実際に実行される可能性があることを意味する。

    方法:以下のコード(テスト呼び出しを削除したもの)をすべてのバージョンで同じライブラリコードが実行されるように単一の.jarファイルにコンパイルしました。ヒープサイズの問題を取り除くために、各繰り返しの各テストが新しいJVM(すなわち、各テストについてscala -cp ...)で呼び出されました。あなたが言われた何

    class t(val i: Int) { 
        def summit(j: Int) = j + i 
    } 
    
    object bar { 
        val biglist:List[Int] = (1 to 1000000).toList 
    
        def summit(i: Int, j:Int) = i+j 
    
        // Simple for loop 
        def forloop: Int = { 
         var result: Int = 0 
         for(i <- biglist) { 
          result += i 
         } 
         result 
        } 
    
        // For loop with a function instead of inline math 
        def forloop2: Int = { 
         var result: Int = 0 
         for(i <- biglist) { 
          result = summit(result,i) 
         } 
         result 
        } 
    
        // for loop with a generated object PER iteration 
        def forloop3: Int = { 
         var result: Int = 0 
         for(i <- biglist) { 
          val t = new t(result) 
          result = t.summit(i) 
         } 
         result 
        } 
    
        // list.reduce with an anonymous function passed in 
        def anonymousfunc: Int = { 
         biglist.reduce((i,j) => {i+j}) 
        } 
    
        // list.reduce with a named function 
        def realfunc: Int = { 
         biglist.reduce(summit) 
        } 
    
        // test calling code excised for brevity. One example given: 
        args(0) match { 
         case "1" => { 
            val start = System.currentTimeMillis() 
            forloop 
            val end = System.currentTimeMillis() 
            println("for="+(end - start)) 
            } 
         ... 
    } 
    
  • +1

    '.reduce'は" for comprehensions "とは関係ありません –

    +0

    補足として、一般的なケースでこのオーバーヘッドを取り除くことを目的としたscalaコンパイラプラグインがあります:https://code.google.com/p/scalacl/wiki/ScalaCLPlugin私はそれを自分で試していない。 –

    +0

    実際、最初の3つのテストはfor-comprehensionsを使用しており、それらのタイミングを 'reduce'と比較しています。 –

    答えて

    12

    「内包について」についての真実だったが、あなたの質問の問題点は、「無名関数」と「内包するための」混ざってきたということです。

    「理解できる」とは、一連の.flatMap,.mapおよび.filterアプリケーションのための構文糖です。削減アルゴリズムをテストしているため、これらの3つの関数を使用して削減アルゴリズムを実装することは不可能であるため、テストケースが正しくありません。

    ここで、 "理解のために" の例です:

    val listOfLists = List(List(1,2), List(3,4), List(5)) 
    val result = 
        for { 
        itemOfListOfLists <- listOfLists 
        itemOfItemOfListOfLists <- itemOfListOfLists 
        } 
        yield (itemOfItemOfListOfLists + 1) 
    assert(result == List(2,3,4,5,6)) 
    

    コンパイラは以下の理解の一部をdesugars:

    val result = 
        listOfLists.flatMap(
        itemOfListOfLists => itemOfListOfLists.map(
         itemOfItemOfListOfLists => itemOfItemOfListOfLists + 1 
        ) 
    ) 
    

    そして、それは無名関数のシンタックスをdesugars:

    val result = 
        listOfLists.flatMap(
        new Function1[List[Int], List[Int]] { 
         override def apply(itemOfListOfLists: List[Int]): List[Int] = 
         itemOfListOfLists.map(
          new Function1[Int, Int] { 
          override def apply(itemOfItemOfListOfLists: Int): Int = 
           itemOfItemOfListOfLists + 1 
          } 
         ) 
        } 
    ) 
    

    デジカラコードからは、メソッドが呼び出されるたびにクラスがインスタンス化されます。それはlistOfListsのすべての入力に対して発生します。だから、より複雑な理解は、あなたが得るFunctionオブジェクトのより多くのインスタンス化です。

    関連する問題