Scalaのコンパイルを3倍速くした話

19
TBD

Transcript of Scalaのコンパイルを3倍速くした話

Page 1: Scalaのコンパイルを3倍速くした話

TBD

Page 2: Scalaのコンパイルを3倍速くした話

自己紹介

•@todesking • ジャバ(バッチ) → Ruby(ソシャゲサーバサイド) → Scala(広告システムサーバサイド)

Page 3: Scalaのコンパイルを3倍速くした話

近況

•ビルドシステムと戦っている http://twitter.com/todesking/status/488675512424734720!

sbt/ivy使えばパッケージ管理の問題を解決してくれるというのは幻覚だったということがわかった、社会は厳しい

Page 4: Scalaのコンパイルを3倍速くした話

何はともあれ•みんな大好きなあの話をします

Page 5: Scalaのコンパイルを3倍速くした話

Scalaのコンパイルが 3倍

速くなった話

Page 6: Scalaのコンパイルを3倍速くした話

問題

https://twitter.com/todesking/status/449393123894906880!Scala、みんなコンパイルの遅さに困ってるが解決手段が無いためコンパイル中に音楽を流すプラグインを作成して慰みを得ているということがわかった。Ruby使ったほうがいいと思う。!

Page 7: Scalaのコンパイルを3倍速くした話

(́・_・`)

• https://twitter.com/todesking/status/449406714123927553

Page 8: Scalaのコンパイルを3倍速くした話

どれくらい遅いか[Info] Compiling 278 Scala sources and 4

Java sources to target/scala-2.10/classes...

.

.

.

.

.

.

.

.[success] Total time: 425 s

Page 9: Scalaのコンパイルを3倍速くした話

?!

• 他のプロジェクト [info] Compiling 234 Scala sources and 5 Java sources to target/scala-2.10/classes... . . [success] Total time: 64 s !

はやい(相対的に)

Page 10: Scalaのコンパイルを3倍速くした話

さすがに死っぽいので 調査しました

• 特定のプロジェクトだけすごく遅い • プロジェクトの設定? • 使用ライブラリ?

Page 11: Scalaのコンパイルを3倍速くした話

hprof: JVM標準 プロファイラ

• jvmオプションつけてsbt compile • 結果はjava.hprof.txtに出力されます

$ sbt -J-agentlib:hprof=cpu=samples,depth=5 \ clean compile

Page 12: Scalaのコンパイルを3倍速くした話

結果見る

• depth=5で荒い解析 • クラスのロードが大量にされている……?

rank self accum count trace method 1 12.04% 12.04% 2158 300093 java.util.zip.ZipFile.getEntry 2 9.90% 21.95% 1775 300065 java.lang.Throwable.fillInStackTrace 3 7.21% 29.16% 1293 303874 java.lang.Class.forName0 4 5.40% 34.56% 967 300200 java.lang.Throwable.fillInStackTrace 5 2.76% 37.32% 495 300061 java.lang.ClassLoader.defineClass1 6 1.66% 38.98% 298 300616 java.lang.ClassLoader.findLoadedClass0 7 1.32% 40.30% 236 303922 scala.collection.IndexedSeqOptimized$class.foreach 8 1.27% 41.57% 228 303902 scala.collection.IndexedSeqOptimized$class.foreach 9 1.27% 42.84% 228 301496 java.net.SocketInputStream.socketRead0 10 1.15% 43.99% 206 300711 java.lang.ClassLoader.findLoadedClass0 11 1.03% 45.02% 184 304106 java.lang.Class.forName 12 0.95% 45.97% 170 300630 java.lang.ClassLoader.loadClass 13 0.86% 46.83% 155 304054 java.lang.Object.hashCode 14 0.84% 47.68% 151 303870 java.io.UnixFileSystem.getBooleanAttributes0 15 0.84% 48.51% 150 303879 java.lang.ClassLoader.loadClass 16 0.81% 49.33% 146 304060 java.security.AccessController.doPrivileged

Page 13: Scalaのコンパイルを3倍速くした話

スタックトレースの詳細

•処理時間上位の処理を見ていく • この処理がどこから呼ばれてるかを知りたい • スタックトレースの深度を増やして再実行

TRACE 300093: java.util.zip.ZipFile.getEntry(ZipFile.java:Unknown line) java.util.zip.ZipFile.getEntry(ZipFile.java:306) java.util.jar.JarFile.getEntry(JarFile.java:226) java.util.jar.JarFile.getJarEntry(JarFile.java:209) sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:840)

Page 14: Scalaのコンパイルを3倍速くした話

結果見る

• depth = 50 • トレース深度を増やしてみる

CPU SAMPLES BEGIN (total = 22833) Tue Jul 15 15:02:57 2014 rank self accum count trace method 1 0.58% 0.58% 132 312316 java.util.zip.ZipFile.getEntry 2 0.56% 1.14% 128 313147 java.util.zip.ZipFile.getEntry 3 0.53% 1.67% 121 313118 java.util.zip.ZipFile.getEntry 4 0.45% 2.12% 103 313343 java.lang.Object.hashCode 5 0.44% 2.56% 100 313093 java.util.zip.ZipFile.getEntry 6 0.42% 2.98% 96 306293 java.net.SocketInputStream.socketRead0 7 0.42% 3.39% 95 312675 java.util.zip.ZipFile.getEntry 8 0.38% 3.78% 87 314506 java.util.zip.ZipFile.getEntry 9 0.36% 4.14% 83 313104 java.lang.Class.forName0 10 0.32% 4.45% 72 306826 java.net.SocketInputStream.socketRead0 11 0.29% 4.74% 66 312099 java.util.zip.ZipFile.getEntry 12 0.28% 5.02% 63 313314 java.lang.Class.forName0 13 0.27% 5.29% 61 313184 java.lang.Class.forName0 14 0.25% 5.54% 57 312970 java.lang.Throwable.fillInStackTrace 15 0.25% 5.79% 57 313083 java.util.zip.ZipFile.getEntry 16 0.24% 6.03% 55 312783 java.lang.Class.forName0

Page 15: Scalaのコンパイルを3倍速くした話

結果見る

•怪しいものが

TRACE 313147: java.util.zip.ZipFile.getEntry(ZipFile.java:Unknown line) java.util.zip.ZipFile.getEntry(ZipFile.java:306) java.util.jar.JarFile.getEntry(JarFile.java:226) java.util.jar.JarFile.getJarEntry(JarFile.java:209) : scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:418) scala.reflect.macros.runtime.Evals$class.eval(Evals.scala:16) scala.reflect.macros.runtime.Context.eval(Context.scala:6) scalikejdbc.SQLInterpolationMacro$.selectDynamic(SQLInterpolationMacro.scala:32) sun.reflect.GeneratedMethodAccessor3.invoke(<Unknown Source>:Unknown line) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) java.lang.reflect.Method.invoke(Method.java:606) scala.tools.nsc.typechecker.Macros$$anonfun$scala$tools$nsc$typechecker$Macros$$macroRuntime$2$$anonfun$apply$5.apply(Macros.scala:542)

Page 16: Scalaのコンパイルを3倍速くした話

原因特定

• scalikejdbcのマクロが原因 • context.eval()が遅い

26 object SQLInterpolationMacro {!27 !28 def selectDynamic[E: c.WeakTypeTag](c: Context)(name: c.Expr[String]): c.Expr[SQLSyntax] = {!29 import c.universe._!30 !31 val nameOpt: Option[String] = try {!32 Some(c.eval(c.Expr[String](c.resetAllAttrs(name.tree.duplicate))))!33 } catch {!34 case t: Throwable => None!35 }!36

Page 17: Scalaのコンパイルを3倍速くした話

ツイッター便利

https://twitter.com/todesking/status/461816289086820352

Page 18: Scalaのコンパイルを3倍速くした話

修正しました

• [success] Total time: 122 s • scalikejdbc 1.7.7からは速いよ https://github.com/scalikejdbc/scalikejdbc/pull/241

Page 19: Scalaのコンパイルを3倍速くした話

学び•基本的にコンパイルは遅い(社会は厳しい) • でもユースケースによっては高速化の余地が! • コンパイラがJVM上で動くので既存のプロファイリングノウハウが使えて便利

• Scalaエコシステムはフロンティア • 目が届いてないとこが残ってるのでコントリビュートチャンスだ

• ツイッター便利