Scala対Java、パフォーマンスとメモリ?[閉まっている]


160

私はScalaを調べたくて、答えを見つけることができないように思えない基本的な質問があります。一般的に、ScalaとJavaの間でパフォーマンスとメモリの使用量に違いはありますか?


3
私は、パフォーマンスが非常に近いと主張することを聞いています。それはあなたが何をしているかに大きく依存していると思います。(Java vs Cの場合と同様)
Peter Lawrey、

質問のこれらの種類への答えは「それが依存」である-システムY.プラスVSシステムXの事実上すべての比較のために、これはの複製であるstackoverflow.com/questions/2479819/...
ジェームズ・ムーア

回答:


261

Scalaは、それを実現せずに大量のメモリを非常に簡単に使用できるようにします。これは通常非常に強力ですが、時々迷惑なことがあります。たとえば、文字列の配列(と呼ばれるarray)と、それらの文字列からファイル(と呼ばれるmapping)へのマップがあるとします。マップ内にあり、2を超える長さの文字列から取得されるすべてのファイルを取得するとします。Javaでは、

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}

ふew!ハードワーク。Scalaでは、同じことを行う最もコンパクトな方法は次のとおりです。

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

かんたん!ただし、コレクションがどのように機能するかを十分に理解していない限り、この方法で追加の中間配列(with filter)、および配列のすべての要素用の追加オブジェクト(with mapping.get、オプション)。また、2つの関数オブジェクト(1つはフィルター用、もう1つはflatMap用)も作成しますが、関数オブジェクトは小さいため、これが大きな問題になることはめったにありません。

したがって、基本的に、メモリ使用量はプリミティブレベルで同じです。しかし、Scalaのライブラリには、膨大な数の(通常は有効期間が短い)オブジェクトを非常に簡単に作成できる強力なメソッドが多数あります。ガベージコレクターは通常、その種のガベージにはかなり優れていますが、使用されているメモリに完全に気付かない場合、JavaよりもScalaの方が早く問題に直面するでしょう。

コンピュータ言語ベンチマークゲームのScalaコードは、Javaのようなパフォーマンスを得るために、Javaのようなスタイルで記述されているため、Javaのようなメモリ使用量を持っています。これはScalaで行うことができます。コードを高性能Javaコードのように書くと、それは高性能Scalaコードになります。(あなたはあり、より慣用Scalaのスタイルでそれを書いて、まだ良いパフォーマンスを得ることができるが、それは具体的に依存します。)

プログラミングに費やした時間ごとに、私のScalaコードは通常Javaコードより高速であることを追加する必要があります。Scalaでは、手間をかけずに退屈でパフォーマンスが重要でない部分を実行でき、アルゴリズムの最適化とパフォーマンスが重要な部分のコード。


172
最後の段落の+1。それは考慮の外に残っている重要なポイントだ遠く、あまりにも頻繁に。
Kevin Wright、

2
ビューはあなたが言及する問題に大いに役立つと思いました。または、配列では特にそうではありませんか?
Nicolas Payette、

1
@Kevin Wright-「それはあまりにも頻繁に考慮から外されている重要なポイントです」-それは言うのが簡単で、実証するのが難しい何かであり、他のあまり熟練していないものではなく、レックスカーのスキルについて私たちに教えてくれます。
igouy

1
>>最高のパフォーマンスを備えた慣用的なスタイル<<ベンチマークゲームには、「最高の」パフォーマンスを実現しない慣用的なScalaプログラムの余地があります。
igouy

2
@RexKerr-Javaの例では、可能な文字列ごとにマッピングキーを2回検索しませんか?つまり、データセットごとに異なる方法で最適化されていますか?
Seth

103

私は新しいユーザーなので、上記のRex Kerrの回答にコメントを追加できません(新しいユーザーに「コメント」ではなく「回答」を許可するのは非常に奇妙な規則です)。

上記のRexの人気のある回答の「ほら、Javaはとても冗長で、大変な作業」というほのめかしに応えるためだけにサインアップしました。もちろん、より簡潔なScalaコードを記述することもできますが、Javaの例は明らかに肥大化しています。ほとんどのJava開発者は次のようにコーディングします。

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}

そしてもちろん、Eclipseが実際のタイピングのほとんどを行わないふりをして、保存されたすべての文字が本当に優れたプログラマーになるとしたら、次のようにコーディングできます。

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));

これで、完全な変数名と中括弧を入力するのにかかる時間を節約できただけでなく(アルゴリズムを深く考えるためにさらに5秒費やすことができます)、難読化コンテストにコードを入力して追加の現金を獲得することもできます休日。


7
「今月の流行語」クラブの会員ではないのはなぜですか?いいコメント。私は特に最後の段落を読んで楽しんだ。
ステパニアン

21
見事に入れました!私は、膨らんだJavaコードの後に​​、慎重に作成された簡潔なScala(または他のFP言語)の例が続き、それからScalaはJavaより優れているに違いないと急いで結論付けました。とにかくScalaで重要なことを書いた人は誰でもいます!;-) Twitterとは言わないでください
chrisjleu

2
さて、Rexのソリューションは配列にメモリを事前に割り当てます。これにより、コンパイルされたコードの実行が高速になります(この方法では、配列が大きくなるにつれて、JVMに定期的に配列を再割り当てさせるためです)。より多くのタイピングが関わっていたとしても、パフォーマンスに関しては勝者になる可能性があります。
Ashalynd、2015

5
私たちがそれをしている間、java8では次のようになりますArrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
。–ベニール

2
ScalaがJavaよりも「優れている」点は、より一般的なパターンをタイプ(モナド、ファンクターなど)として簡単に表現できるようにする拡張型システム機能です。これにより、Javaでよく発生するような、非常に厳密なコントラクトが原因で邪魔にならない型を作成できます。コードの実際のパターンに基づいていない厳密なコントラクトが、コードを適切に単体テストするために責任の逆転パターンが必要な理由です(依存性注入が最初に頭に浮かび、XML地獄がもたらす)。アドレス。柔軟性がもたらす簡潔さは単なるボーナスです。
ジョサイア2016年

67

JavaのようにScalaを記述すれば、ほぼ同一のメトリックを持つほぼ同一のバイトコードが出力されることが期待できます。

不変のオブジェクトと高次関数を使用して、より「慣用的に」記述します。少し遅く、少し大きくなります。この経験則の1つの例外は、タイプparamsが@specialisedアノテーションを使用する汎用オブジェクトを使用する場合、ボックス化/ボックス化解除を回避することによりJavaのパフォーマンスを超えることができるさらに大きなバイトコードを作成します。

言及する価値があるのは、並列で実行できるコードを作成する場合、メモリの増加/速度の低下は避けられないトレードオフであることです。慣用的なScalaコードは、典型的なJavaコードよりも本質的にはるかに宣言的であり、.par完全な並列性からはわずか4文字()離れていることがよくあります。

だから

  • Scalaコードは、シングルスレッドのJavaコードよりも1.25倍長くかかります
  • 4コアに簡単に分割できます(ラップトップでも一般的になりました)。
  • (1.24 / 4 =)0.3125xの元のJavaの並列実行時間の場合

次に、Scalaコードは比較的25%遅くなりますか、それとも3倍速くなりますか?

正しい答えは、「パフォーマンス」の定義方法によって異なります:)


4
ちなみに、これは.par2.9にあることを述べておきたいと思います。
Rex Kerr、

26
>> Scalaコードは比較的25%遅くなりますか、それとも3倍速くなりますか?<<マルチスレッドのJavaコードと比較すると、どうしてですか?
igouy

17
@igouy-重要なのは、架空のコードが存在しないことです。「より高速な」Javaコードの必須の性質により、並列化がはるかに困難になり、コストと利益の比率がまったく発生しない可能性があります。一方、慣用的なScalaは、本質的にはるかに宣言的であるため、ほんのわずかな変更を加えるだけで、同時に実行することができます。
Kevin Wright、

7
並行Javaプログラムの存在は、典型的な Javaプログラムが並行性に容易に適合できることを意味しません。どちらかと言えば、特定のfork-joinスタイルはJavaでは特にまれであり、明示的にコード化する必要があると思いますが、最小の含まれる値やコレクション内の値の合計を見つけるなどの単純な操作は、並列で簡単に実行できます。 Scalaでは単にを使用し.parます。
Kevin Wright、

5
いいえ、私はそうしないかもしれません。この種のものは多くのアルゴリズムの基本的な構成要素であり、言語および標準ライブラリ(標準ライブラリだけでなく、すべてのプログラムが使用するものと同じ標準ライブラリ)でこのような低レベルで存在することを確認すると、言語を選択するだけで、同時並行性に近づきます。たとえば、コレクションのマッピングは本質的に並列化に適しており、mapメソッドを使用しないScalaプログラムの数は非常に少なくなります。
Kevin Wright、

31

コンピュータ言語ベンチマークゲーム:

速度テスト java / scala 1.71 / 2.25

メモリテスト java / scala 66.55 / 80.81

したがって、このベンチマークは、Javaが24%高速で、Scalaが21%多いメモリを使用することを示しています。

全体としては大したことではなく、ほとんどの時間がデータベースとネットワークによって消費される実際のアプリでは問題になりません。

結論: Scalaがあなたとあなたのチーム(そしてあなたが去ったときにプロジェクトを引き継いでいる人々)の生産性を高めるなら、あなたはそれのために行くべきです。


34
コードサイズ java / scala 3.39 / 2.21
hammar

22
このような数字には注意してください。実際にはほとんど意味がないのに、かなり正確に聞こえます。Scalaは常に24%高速化するJavaよりも平均であるかのようになど、ありません
ジェスパー

3
Afaikが引用した数字は、その逆を示しています。JavaはScalaより24%高速です。しかし、あなたが言うように-それらはマイクロベンチマークであり、実際のアプリで起こっていることと一致する必要はありません。そして、異なる言語での異なる方法または問題解決は、最終的には比較可能なプログラムが少なくなる可能性があります。
ユーザー不明

9
「Scalaがあなたとあなたのチームを作るなら...」
結論

ベンチマークゲームのヘルプページには、「2つの言語の実装でプログラムの速度とサイズを比較する」方法の例が示されています。スカラ座とJavaのための適切な比較Webページがある- shootout.alioth.debian.org/u64q/scala.php
igouyは、

20

私がコメントしたRex Kerrの例の間には明らかなパフォーマンスの違いがあるようですが、タイトループに関してこの質問に答える人もいます。

この回答は、設計上の欠陥としてタイトループ最適化の必要性を調査する可能性がある人を対象としています。

私はScalaに比較的慣れていません(約1年ほど)が、これまでのところ、設計、実装、実行の多くの側面を比較的簡単に据え置くことができるという感触があります(十分なバックグラウンドでの読み取りと実験で)。

遅延設計機能:

遅延実装機能:

遅延実行機能:(申し訳ありませんが、リンクはありません)

  • スレッドセーフな遅延値
  • 名前渡し
  • モナディックのもの

私にとってこれらの機能は、高速でタイトなアプリケーションへの道を踏み出すのに役立つ機能です。


Rex Kerrの例は、実行のどの側面が据え置かれるかが異なります。Javaの例では、メモリの割り当ては、Scalaの例がマッピングルックアップを延期するサイズが計算されるまで延期されます。私には、まったく異なるアルゴリズムのように見えます。

ここに私が思うのは、彼のJavaの例に相当するリンゴからリンゴに相当するものです:

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})

中間コレクション、Optionインスタンスなどはありません。これにより、コレクションタイプも保持されるため、bigEnoughの型はArray[File]-です。実装は、おそらくカー氏のJavaコードArraycollect実行に沿って何かを実行します。

上記の据え置き設計機能により、ScalaのコレクションAPI開発者は、APIを壊すことなく、将来のリリースでその高速なアレイ固有の収集実装を実装することもできます。これは私がスピードへの道を歩むことで言及していることです。

また:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)

withFilter私はここの代わりに使用したこと方法filter修正中間収集問題をが、Optionインスタンスの問題がまだあります。


Scalaでの単純な実行速度の1つの例はロギングです。

Javaでは、次のように記述します。

if (logger.isDebugEnabled())
    logger.debug("trace");

Scalaでは、これは次のとおりです。

logger.debug("trace")

Scalaでデバッグするメッセージパラメーターのタイプは " => String"です。これは、評価時に実行されるパラメーターなしの関数と見なされますが、ドキュメントでは名前渡しを呼び出します。

EDIT {Scalaの関数はオブジェクトなので、ここに追加のオブジェクトがあります。私の仕事では、些細なオブジェクトの重みは、ログメッセージが不必要に評価される可能性を取り除く価値があります。}

これによってコードが速くなるわけではありませんが、コードが速くなる可能性が高くなり、他の人のコードを一斉に調べてクリーンアップする経験が少なくなる可能性があります。

私にとって、これはScala内で一貫したテーマです。


ハードコードは、Scalaが少しヒントになるものの、なぜScalaが高速であるかをキャプチャできません。

これは、Scalaでのコードの再利用とコード品質の上限の組み合わせだと思います。

Javaでは、素晴らしいコードはしばしば不可解な混乱になることを余儀なくされ、ほとんどのプログラマーがそれを使用することができないので、実稼働品質のAPI内では実際に実行可能ではありません。

Scalaが私たちの間のアインシュタインに、DSLによって表現される可能性のあるはるかに有能なAPIを実装できるようにしてくれることを期待しています。ScalaのコアAPIはすでにこの道を進んでいます。


ログ記録は、Scalaのパフォーマンスの落とし穴の良い例です。logger.debug( "trace")は、パラメーターのない関数の新しいオブジェクトを作成します。
jcsahnwaldtがモニカを復活させる

確かに-それは私の関連するポイントにどのように影響しますか?
セス

前述のオブジェクトは、効率化のために透過的なIoC制御構造を作成するためにも使用できます。はい、同じ結果が理論的にはJavaで可能ですが、コードの記述方法に劇的な影響を与えたり難読化したりする可能性があります。したがって、ソフトウェア開発の多くの要素を先延ばしするScalaのコツは、より高速なコードへの移行に役立つという私の主張-可能性が高い実際には、ユニットのパフォーマンスがわずかに速い場合と比べて高速です。
セス

わかりました。これをもう一度読んだので、「単純な実行速度」と書きました。メモを追加します。良い点:)
Seth

3
予測可能なifステートメント(スーパースカラープロセッサでは基本的に無料)とオブジェクトの割り当て+ガベージ。Javaコードは明らかに高速です(条件を評価するだけで、実行はログステートメントに到達しません。)「私の仕事では、重要なオブジェクトの重みは、ログメッセージが不必要に評価される可能性を取り除く価値があります。 」
Eloff 2014


10

JavaとScalaはどちらもJVMバイトコードにコンパイルされるため、違いはそれほど大きくありません。あなたが得ることができる最良の比較は、おそらくコンピュータ言語ベンチマークゲームであるでしょう。それは本質的にJavaとScalaの両方が同じメモリ使用量を持っていると言っています。リストされているいくつかのベンチマークでは、ScalaはJavaよりもわずかに遅いだけですが、それは単にプログラムの実装が異なるためである可能性があります。

実際には、どちらも非常に近いので、心配する必要はありません。Scalaのようなより表現力豊かな言語を使用することで得られる生産性の向上は、最小限のパフォーマンスヒット(もしあれば)よりもはるかに価値があります。


7
私はここで論理的な誤りを見つけました:両方の言語はバイトコードにコンパイルされますが、経験豊富なプログラマーと初心者-彼らのコードもバイトコードにコンパイルされます-同じバイトコードではないので、違いはそれほど大きくないという結論、間違っている可能性があります。実際、以前は、whileループは、意味的に同等のforループよりもはるかに高速でありました(私が正しく覚えていれば、今日ははるかに優れています)。もちろん、両方ともバイトコードにコンパイルされました。
不明なユーザー、

@user unknown-「whileループは、意味的に同等のforループよりもはるかに高速である可能性があります」-これらのScalaベンチマークゲームプログラムは、whileループで記述されていることに注意してください。
igouy

@igouy:このマイクロベンチマークからの結果については話しませんでしたが、議論についてです。私が見せたかった問題のステートメントに対するJava and Scala both compile down to JVM bytecode, a soと組み合わされた真のステートメントは、これは修辞的なトリックであり、議論の余地のある結論ではないということです。diffence isn't that big.so
不明なユーザー、

3
驚くほど高い票で驚くほど間違った答え。
shabunc 2013年

4

Javaの例は、典型的なアプリケーションプログラムのイディオムではありません。このような最適化されたコードは、システムライブラリメソッドで見つかる場合があります。しかし、それは正しいタイプの配列、つまりFile []を使用し、IndexOutOfBoundsExceptionをスローしません。(カウントと追加のための異なるフィルター条件)。私のバージョンは(Eclipseで単一のキーを押すために2秒を節約することによって導入されたバグを検索するのに1時間費やすのが好きではないため、常に(!)中括弧が付いています):

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

しかし、私の現在のプロジェクトから、他の多くの醜いJavaコードの例を紹介できます。一般的な構造と動作を除外することにより、コーディングの一般的なコピーと変更のスタイルを回避しようとしました。

私の抽象DAO基本クラスには、共通のキャッシュメカニズム用の抽象内部クラスがあります。すべての具象モデルオブジェクトタイプには、抽象DAO基本クラスのサブクラスがあります。内部クラスは、データベースからロードされたときにビジネスオブジェクトを作成するメソッドの実装を提供するためにサブクラス化されます。(独自のAPIを介して別のシステムにアクセスするため、ORMツールを使用できません。)

このサブクラス化とインスタンス化のコードはJavaではまったく明確ではなく、Scalaでは非常に読みやすいでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.