2017-01-29 4 views
2

私はScalaに慣れていません。IndexedSeqから大きなマップを作成しようとしています。機能的なスタイルマップの作成は、命令型のJavaのものよりもはるかに遅いという言葉が見つかりました。これまでのところ、私は、Scalaの機能スタイルコードだけではなく、遅くても命令的であることを知っていました。何が間違っているのですか、なぜ私のScalaコードが数倍遅くなっていますか?私の自宅のコンピュータ上で、それは220ミリ秒で実行されます。(Javaの)と460ミリ秒。(スカラ)Scalaの必須スタイルのマップ作成スニペットがJavaのスニペットより遅いのはなぜですか?

Scalaのversion

private val testSize: Int = 1000000 

    val seq: IndexedSeq[Int] = for (i <- 0 until testSize) yield Random.nextInt() 

    val warmupMapt0 = System.nanoTime() 

    var warmupMap: mutable.HashMap[Int, Int] = new mutable.HashMap[Int, Int] 
    warmupMap.sizeHint(testSize) 
    for (i <- 0 until testSize) warmupMap.put(i, seq(i)) 

    val t0 = System.nanoTime() 
    var map: mutable.HashMap[Int, Int] = new mutable.HashMap[Int, Int] 
    map.sizeHint(testSize) 
    for (i <- 0 until testSize) map.put(i, seq(i)) 
    println((System.nanoTime() - t0)/ 1000000 + " ms.") 

のJava version

private static final int TEST_SIZE = 1_000_000; 

public static void main(String[] args) { 

    int[] ar = new int[TEST_SIZE]; 
    Random random = new Random(); 
    for (int i = 0; i < TEST_SIZE; i++) { 
     ar[i] = random.nextInt(); 
    } 

    Map<Integer, Integer> warmupMap = new HashMap<>(TEST_SIZE); 
    for (int i = 0; i < TEST_SIZE; i++) { 
     warmupMap.put(i, ar[i]); 
    } 

    Map<Integer, Integer> map = new HashMap<>(TEST_SIZE); 
    long t0 = System.nanoTime(); 
    for (int i = 0; i < TEST_SIZE; i++) { 
     map.put(i, ar[i]); 
    } 
    System.out.println((System.nanoTime() - t0)/1_000_000 + " ms."); 
} 
+0

これはあなたを助けるかもしれないhttp://stackoverflow.com/questions/9799085/scala-perf-why-is-this-scala-app-30x-slower-than-the-equivalent-java-app?rq=1 –

+0

@ Jean-BaptisteYunèsええ、私はそれを見ました。なぜワープアップの前にval warmupMapt0 = System.nanoTime()を追加して、コンソールから実行しようとしましたが、それは助けになりませんでした。 – MaxNevermind

+0

タイミング結果を追加できますか? – maasg

答えて

0

それはおそらくfor理解です。 Scalaでは、彼らはJavaのループであるforループとはまったく異なる方法で動作し、JVMが十分に最適化できないコードを生成します。例えば、 http://www.scalablescala.com/roller/scala/entry/why_is_using_for_foreachまたはhttp://downloads.typesafe.com/website/presentations/ScalaDaysSF2015/T2_Rytz_Backend_Optimizer.pdf(スライド37から開始)。ループをwhileループまたはhttp://scala-blitz.github.io/,https://github.com/non/spireまたはhttps://github.com/nativelibs4java/scalaxy-streamsのようなマクロライブラリを使用して回避することができます。

+0

whileループとの大きな違いはありません。 – MaxNevermind

3

問題の原因の1つはIndexedSeqの使用だと思います。これは、デフォルトでは一般的にスマートコレクションであるVectorによって実装されていますが、あなたのケースでは、数値の「配列」を作成し、インデックスでアクセスするよりもかなり大きな定数を追加します。あなたはJavaの対応により同等であるためにあなたのコードを希望の場合は、次のコードは、答えのようになります。

val ar = new Array(TestSize) 
for (i <- 0 until TestSize) ar(i) = Random.next() 

私はforeachの約どこかで読んで基本的に十分なウォームアップ与えられた場所を、見つけることができない、最適化をループするforeachループを実行しますwhileループと比較して効率が似ているはずですが、渡された関数をインライン化することができます。

編集

コードをさらに簡素化することができる。

コメントにアレクセイ・ロマノフが提案
val ar = Array.fill(TestSize)(Random.next()) 

+0

家に帰ると私はそれを試してみます。私のオフィスノートパソコンはさらに大きな違いを示しています~100ms \ 1000ms、私はベンチマークについて何かを読む必要があると思います。異なるハードウェア/環境でのこのようなさまざまな結果が期待されます。 – MaxNevermind

+1

最後の文:残念ながら、いいえ(またはまだ)。スライド37から始まるhttp://downloads.typesafe.com/website/presentations/ScalaDaysSF2015/T2_Rytz_Backend_Optimizer.pdfを参照してください。2.12の新しいオプティマイザはインライン展開を許可しますが、デフォルトでは実行しません(ウォームアップはそれとは無関係です)。 –

+0

'val ar = Array.fill(TestSize)(Random.next())'に単純化することもできます。 –

関連する問題