scalaコードベースがあります。( https://opensource.ncsa.illinois.edu/confluence/display/DFDL/Daffodil%3A+Open+Source+DFDL )
scalaコードの70K行。我々はscala 2.11.7
コンパイル-編集-コンパイル-テスト-デバッグのサイクルは小さな変更には長すぎるため、開発は難しくなっています。
増分再コンパイル時間は1分になる場合があり、これは最適化がオンになっていない場合です。時々長くなります。そして、それはファイルへの非常に多くの変更を編集していないということです。場合によっては、非常に小さな変更でも大きな再コンパイルが発生することがあります。
だから私の質問:コードを整理することで何ができますか、それはコンパイル時間を改善しますか?
たとえば、コードを小さなファイルに分解しますか?これは役立ちますか?
たとえば、より小さなライブラリですか?
たとえば、暗黙の使用を回避しますか? (非常に少ない)
たとえば、特性の使用を回避しますか? (トンあります)
たとえば、大量の輸入を避けますか? (私たちはたくさんあります-パッケージの境界はこの時点ではかなり混oticとしています)
それとも、これに関して私ができることは本当に何もないのですか?
この非常に長いコンパイルは、依存関係による再コンパイルの膨大な量に何らかの原因があるように感じ、false依存関係を減らす方法を考えています。 ..しかし、それは単なる理論です
他の誰かが少しずつ光を当てることを期待していますsomethingこれにより、インクリメンタルな変更のコンパイル速度が向上します。
scalaコンパイラーのフェーズと、ソースコードからのコメントのわずかに編集されたバージョンを示します。このコンパイラーは、型チェックとより多くの変換に重点が置かれる点で異常であることに注意してください他のコンパイラには、最適化、レジスタ割り当て、IRへの変換のための多くのコードが含まれています。
いくつかのトップレベルのポイント:多くのツリーの書き換えがあります。各フェーズは、前のフェーズからツリーを読み取り、それを新しいツリーに変換する傾向があります。対照的に、シンボルはコンパイラーの存続期間を通じて意味を持ちます。したがって、ツリーはシンボルへのポインタを保持しますが、逆も同様です。シンボルを書き換える代わりに、フェーズが進むにつれて新しい情報がシンボルに付加されます。
グローバルのフェーズのリスト:
analyzer.namerFactory: SubComponent,
analyzer.typerFactory: SubComponent,
superAccessors, // add super accessors
pickler, // serializes symbol tables
refchecks, // perform reference and override checking,
translate nested objects
liftcode, // generate reified trees
uncurry, // uncurry, translate function values to anonymous
classes
tailCalls, // replace tail calls by jumps
explicitOuter, // replace C.this by explicit outer pointers,
eliminate pattern matching
erasure, // erase generic types to Java 1.4 types, add
interfaces for traits
lambdaLift, // move nested functions to top level
constructors, // move field definitions into constructors
flatten, // get rid of inner classes
mixer, // do mixin composition
cleanup, // some platform-specific cleanups
genicode, // generate portable intermediate code
inliner, // optimization: do inlining
inlineExceptionHandlers, // optimization: inline exception handlers
closureElimination, // optimization: get rid of uncalled closures
deadCode, // optimization: get rid of dead cpde
if (forMSIL) genMSIL else genJVM, // generate .class files
scalaコンパイラー の回避策
したがって、scalaコンパイラはJavaコンパイラよりも多くの作業を行う必要がありますが、特にScalaコンパイラは大幅に遅くなります。
さらにJavaおよびScalaコンパイラはソースコードをJVMバイトコードに変換し、最適化をほとんど行いません。ほとんどの最新のJVMでは、プログラムのバイトコードが実行されると、実行されているコンピュータアーキテクチャのマシンコードに変換されます。これはジャストインタイムコンパイルと呼ばれますが、ジャストインタイムコンパイルでは高速である必要があるため、コード最適化のレベルは低くなります。再コンパイルを回避するために、いわゆるHotSpotコンパイラは、頻繁に実行されるコードの部分のみを最適化します。
プログラムは、実行されるたびにパフォーマンスが異なる場合があります。同じJVMインスタンスで同じコード(メソッドなど)を複数回実行すると、実行の間に特定のコードが最適化されているかどうかによってパフォーマンスが大きく異なる場合があります。さらに、コードの一部の実行時間を測定すると、JITコンパイラ自体が最適化を実行していた時間が含まれるため、一貫性のない結果が得られます。
パフォーマンス低下の一般的な原因の1つは、プリミティブ型を引数としてジェネリックメソッドと頻繁なGCに渡すときに暗黙的に行われるボックス化とボックス化解除です。
上記の影響を測定中に回避するには、より積極的な最適化を行うサーバーバージョンのHotSpot JVMを使用して実行する必要があります。Visualvmは、JVMアプリケーションのプロファイリングに最適です。これは、いくつかのコマンドラインJDKツールと軽量プロファイリング機能を統合したビジュアルツールです。ただし、scala abstracionsは非常に複雑であり、残念ながらVisualVMは、 Scalaコレクションのメソッドである多くのexists
およびforall
を使用します。これらのコレクションは、述語、述語をFOLに渡し、シーケンス全体を最大化してパフォーマンスを最大化できます。
また、モジュールを協調的で依存性の少ないものにすることも実行可能なソリューションです。中間コードの生成は、時々マシンに依存し、さまざまなアーキテクチャはさまざまな結果をもたらします。
代替案:Typesafeは、高速インクリメンタルコンパイラをsbtから分離し、maven /他のビルドツールがそれを使用できるようにするZincをリリースしました。したがって、scala mavenプラグインでZincを使用すると、コンパイルが非常に高速になります。
簡単な問題:整数のリストが与えられたら、最大のものを削除します。注文は必要ありません。
以下は、ソリューションのバージョンです(私が推測する平均)。
def removeMaxCool(xs: List[Int]) = {
val maxIndex = xs.indexOf(xs.max);
xs.take(maxIndex) ::: xs.drop(maxIndex+1)
}
Scala慣用的で簡潔で、いくつかのNiceリスト関数を使用します。非常に非効率的です。少なくとも3回または4回リストを走査します。
ここで、Javaに似たソリューションを検討してください。また、合理的なJava開発者(またはScala初心者)が書くものでもあります。
def removeMaxFast(xs: List[Int]) = {
var res = ArrayBuffer[Int]()
var max = xs.head
var first = true;
for (x <- xs) {
if (first) {
first = false;
} else {
if (x > max) {
res.append(max)
max = x
} else {
res.append(x)
}
}
}
res.toList
}
完全に非Scalaの慣用的、非機能的、非簡潔ですが、非常に効率的です。それは一度だけリストをたどります!
そのため、トレードオフにも優先順位を付ける必要があり、場合によっては、Java開発者が他にいない場合は開発者のようなことをする必要があります。
役立つかもしれないいくつかのアイデア-あなたのケースと開発スタイルに依存します:
~compile
SBTまたはIDEによって提供されます。私は、オブジェクト指向設計の主な問題の1つに触れています(オーバーエンジニアリング)。クラスオブジェクトの特性の階層を平坦化し、クラス間の依存関係を減らす必要があると思います。パッケージを異なるjarファイルにブレーキし、それらを「凍結」された新しいライブラリに集中するミニライブラリとして使用します。
OO over-engineeringに反対するケースを作っているBrian Willのビデオもチェックしてください。
つまり https://www.youtube.com/watch?v=IRTfhkiAqPw (良い点が取れる)
私は彼に100%同意しませんが、それはオーバーエンジニアリングに対して良いケースになります。
お役に立てば幸いです。
Fast Scala Compiler を使用してみてください。
(例えば@tailrec
注釈)、あなたがどれだけ勇敢であるかに応じて、 Dotty をいじることもできます。