私はScalaを調べたくて、答えを見つけることができないように思えない基本的な質問があります。一般的に、ScalaとJavaの間でパフォーマンスとメモリの使用量に違いはありますか?
私はScalaを調べたくて、答えを見つけることができないように思えない基本的な質問があります。一般的に、ScalaとJavaの間でパフォーマンスとメモリの使用量に違いはありますか?
回答:
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では、手間をかけずに退屈でパフォーマンスが重要でない部分を実行でき、アルゴリズムの最適化とパフォーマンスが重要な部分のコード。
私は新しいユーザーなので、上記の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秒費やすことができます)、難読化コンテストにコードを入力して追加の現金を獲得することもできます休日。
Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
JavaのようにScalaを記述すれば、ほぼ同一のメトリックを持つほぼ同一のバイトコードが出力されることが期待できます。
不変のオブジェクトと高次関数を使用して、より「慣用的に」記述します。少し遅く、少し大きくなります。この経験則の1つの例外は、タイプparamsが@specialised
アノテーションを使用する汎用オブジェクトを使用する場合、ボックス化/ボックス化解除を回避することによりJavaのパフォーマンスを超えることができるさらに大きなバイトコードを作成します。
言及する価値があるのは、並列で実行できるコードを作成する場合、メモリの増加/速度の低下は避けられないトレードオフであることです。慣用的なScalaコードは、典型的なJavaコードよりも本質的にはるかに宣言的であり、.par
完全な並列性からはわずか4文字()離れていることがよくあります。
だから
次に、Scalaコードは比較的25%遅くなりますか、それとも3倍速くなりますか?
正しい答えは、「パフォーマンス」の定義方法によって異なります:)
.par
2.9にあることを述べておきたいと思います。
.par
ます。
map
メソッドを使用しないScalaプログラムの数は非常に少なくなります。
コンピュータ言語ベンチマークゲーム:
速度テスト java / scala 1.71 / 2.25
メモリテスト java / scala 66.55 / 80.81
したがって、このベンチマークは、Javaが24%高速で、Scalaが21%多いメモリを使用することを示しています。
全体としては大したことではなく、ほとんどの時間がデータベースとネットワークによって消費される実際のアプリでは問題になりません。
結論: Scalaがあなたとあなたのチーム(そしてあなたが去ったときにプロジェクトを引き継いでいる人々)の生産性を高めるなら、あなたはそれのために行くべきです。
私がコメントした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コードArray
のcollect
実行に沿って何かを実行します。
上記の据え置き設計機能により、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はすでにこの道を進んでいます。
JavaとScalaはどちらもJVMバイトコードにコンパイルされるため、違いはそれほど大きくありません。あなたが得ることができる最良の比較は、おそらくコンピュータ言語ベンチマークゲームであるでしょう。それは本質的にJavaとScalaの両方が同じメモリ使用量を持っていると言っています。リストされているいくつかのベンチマークでは、ScalaはJavaよりもわずかに遅いだけですが、それは単にプログラムの実装が異なるためである可能性があります。
実際には、どちらも非常に近いので、心配する必要はありません。Scalaのようなより表現力豊かな言語を使用することで得られる生産性の向上は、最小限のパフォーマンスヒット(もしあれば)よりもはるかに価値があります。
Java and Scala both compile down to JVM bytecode,
a so
と組み合わされた真のステートメントは、これは修辞的なトリックであり、議論の余地のある結論ではないということです。diffence isn't that big.
so
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では非常に読みやすいでしょう。