Scalaプロジェクトの ビルド速度改善
by
kenji yoshida
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Scalaプロジェクトの ビルド速度改善 ScalaMatsuri 2024 2024-06-08 1 / 78
Slide 2
Slide 2 text
X(旧twitter) @xuwei_k github @xuwei-k blog https://xuwei-k.hatenablog.com 2 / 78
Slide 3
Slide 3 text
誰? Scala 2.8出る直前くらいから使ってる 2009年 仕事でのScala歴が13年以上? Scala 2本体その他色々なScala関連OSSの権限持 って貢献してる 3 / 78
Slide 4
Slide 4 text
前提の話 build速度とは? 扱わないこと、方向性 速度改善の目的 4 / 78
Slide 5
Slide 5 text
前提の話 どれが効果があるか?優先度高いか?は難しい 手法をひたすら色々紹介していく流れ 5 / 78
Slide 6
Slide 6 text
前提の話 そもそも普通にボトルネック探したらScalaやsbt に限らない部分の最適化の余地の方が多い場合も 多々 6 / 78
Slide 7
Slide 7 text
OSSか、それ以外か よほど巨大で活発でなければOSSではあまり問題に ならない? OSSの種類にもよる 広い意味でIOがないライブラリなら楽 7 / 78
Slide 8
Slide 8 text
OSSか、それ以外か OSSは大抵GitHub Actionsなどで無料の範囲で済 む よってOSSは富豪的に並列実行してることが多々 普通の?プロダクト開発とは全然異なる 8 / 78
Slide 9
Slide 9 text
普通の?プロダクト開発 通常CIのお金的コストがかかる 複数人で活発に開発 される場合がある CIは普通は速く終わるほど良いはず 9 / 78
Slide 10
Slide 10 text
扱うこと compileの速度とtest実行、その他含めた速度? ローカルにも恩恵はあるが主にCIでの速度 10 / 78
Slide 11
Slide 11 text
扱うこと 自分の経験や普段使用してる技術を元に話すので、 全てを網羅してるわけではない 主にsbt前提 他でも使えるがGitHub Actions(のような)CIの システム想定 11 / 78
Slide 12
Slide 12 text
目的やトレードオフ 速くなればお金はかかっていいのか? 速くなればビルドの設定が複雑になってもいいの か? 12 / 78
Slide 13
Slide 13 text
目的やトレードオフ 一時的に速くなっても継続的にメンテ不可能なほど ビルドが複雑になってしまったら駄目では 13 / 78
Slide 14
Slide 14 text
目的やトレードオフ 使うお金を増やさずに速度も全体の効率も改善した い? build速度改善のために、どの程度人間の工数かけ るのか? 14 / 78
Slide 15
Slide 15 text
扱わないこと Bazel gitのproject分ける Compiler内部の深い話 15 / 78
Slide 16
Slide 16 text
Bazelのような 差分buildとtest 究極的な理想としてはこれ 本気で巨大なmonorepoで頑張るなら潜在的な可能 性としては1番良い? 16 / 78
Slide 17
Slide 17 text
Bazelのような 差分buildとtest 2024年の時点のsbtで正確に徹底的にやるには厳し い部分が多い sbt 2にはそういう機能をもっと入れたいらしい 17 / 78
Slide 18
Slide 18 text
GitのProject分ける? monorepoのメリットは捨てたくない 18 / 78
Slide 19
Slide 19 text
基本 sbtのproject分ける ボトルネックの予想、把握、計測 sbtの設定見直す CIの設定見直す 19 / 78
Slide 20
Slide 20 text
sbtのproject を適切に分ける 一番大事かもしれない Scala compiler内部は一部しか並列化されていな い 大まかにはScala 2も3も同様 20 / 78
Slide 21
Slide 21 text
sbtのproject を適切に分ける CPUコアやメモリやネットワーク、ディクスのIOが ボトルネックでないなら、分割すればある程度まで スケールする 分割をし過ぎると別の辛さも発生する 21 / 78
Slide 22
Slide 22 text
sbtのproject を適切に分ける 分割する単位の目安は? 1つのsub projectで数万行単位? 22 / 78
Slide 23
Slide 23 text
sbtのproject を適切に分ける 本来は速度のためではなくコードの意味で分割する べき 個人的な経験として、少なくともsub project数は 百程度ならまだ普通に扱える 23 / 78
Slide 24
Slide 24 text
-Dsbt.traces=true 24 / 78
Slide 25
Slide 25 text
sbtで絶対に使うべき taskごとのtrace機能 https://github.com/sbt/sbt/pull/4576 デフォルトだと細かいtask全部記録して少し扱いづ らいので独自に改造して使ったりしてる 25 / 78
Slide 26
Slide 26 text
sbtで絶対に使うべき taskごとのtrace機能 それをGitHub Actionsでより手軽に記録して見る 方法も https://xuwei- k.hatenablog.com/entry/2024/06/01/103536 26 / 78
Slide 27
Slide 27 text
余計なsub project間の 依存削除や整理 必要ないのにdependsOnしない 必要ないのに "test->test" の依存をしない 27 / 78
Slide 28
Slide 28 text
基本? sbtやScalaはそれぞれ最新版を使う どの程度速度に直結するか?というと場合によるが 28 / 78
Slide 29
Slide 29 text
テストの並列度の理解や設定 https://xuwei- k.hatenablog.com/entry/2020/03/14/234758 https://xuwei- k.hatenablog.com/entry/2024/05/03/101725 29 / 78
Slide 30
Slide 30 text
テストの並列度の理解や設定 sub projectごとにforkするかしないか?がとて も重要 それによって設定も変わる 適切に並列実行するには、DBなどの外部のものが衝 突しないようにする工夫 30 / 78
Slide 31
Slide 31 text
DBなどの外部リソース もはやScalaやsbtに限らない話 適切な単位、タイミングで立ち上げて、それらの CPUやメモリやコネクション数も見てみる 31 / 78
Slide 32
Slide 32 text
DBなどの立ち上げ方 https://testcontainers.com testcontainersは便利だが、多過ぎると効率悪い 32 / 78
Slide 33
Slide 33 text
DBなどの立ち上げ方 複数のミドルウェアを使うなら、それを並列で立ち 上げるみたいな地味な工夫 数秒から数十秒節約 33 / 78
Slide 34
Slide 34 text
sbtはcommandを避け 全てtaskに commandは合成可能ではない commandは並列実行できない taskにしておけば並列実行可能 34 / 78
Slide 35
Slide 35 text
sbtはcommandを避け 全てtaskに sbt "all task1 task2 task3" のようにすれば可能な範囲で並列実行 inputTaskはtoTaskメソッドでtaskに変換可能 35 / 78
Slide 36
Slide 36 text
sbtを立ち上げる回数を 出来るだけ減らす 1回立ち上げるごとに、数秒から、(巨大だと)数十 秒無駄になる もちろん、1回に詰め込み過ぎるとわかりづらくな るデメリットはある 36 / 78
Slide 37
Slide 37 text
wartremoverやscalafix のためのsemanticdb生成 遅い 他に代表的なlinterやcompiler plugin? 🤔 ある程度使うと、compile速度の半分がそれにな る!? 37 / 78
Slide 38
Slide 38 text
wartremover ここ数年自分がほぼメンテしてる 詳細は何度も過去に話をしたのでそちら参照 38 / 78
Slide 39
Slide 39 text
以前のscalafixや wartremoverの話 https://xuwei- k.github.io/slides/Matsuri-2020/ https://xuwei- k.github.io/slides/matsuri-2023/ https://xuwei- k.github.io/slides/wartremover-3/ 39 / 78
Slide 40
Slide 40 text
wartremover Scala 3の場合は、compileと別のタイミングで実 行可能だが、色々辛い compileと同時の時より遅い compileと同時とは稀に違う結果になる? 40 / 78
Slide 41
Slide 41 text
scalafixのための semanticdb生成 scalafixはSyntacticRuleならあまり影響ない 有効と無効を簡単に切り替えられるようにした方が いい 例えばCIでは有効化して、デプロイ時には無効化な ど? 41 / 78
Slide 42
Slide 42 text
sbtの細かいテクニック set key := value は若干遅い? インタラクティブにsettingを変更する方法 ローカルでたまに使うには便利だが 42 / 78
Slide 43
Slide 43 text
sbtの細かいテクニック sbt内部でのトポロジカルソートやり直し? sbt -Dkey=value で指定し sys.props で取得 がいい? あるいは環境変数 43 / 78
Slide 44
Slide 44 text
sbtのExtracted.runTaskメ ソッド避ける そもそもsbtに多少詳しい人しか使わないと思う 同様のrunInputTaskなども 直接taskを実行できて便利だが、競合したり、無駄 に同じtaskが複数回実行される危険がある 44 / 78
Slide 45
Slide 45 text
sbtのrunTaskメソッド避ける 面倒でも必要に応じてDef.taskDynなど使いつ つ、普通のtaskとして依存関係を持たせて定義や実 行するべき 45 / 78
Slide 46
Slide 46 text
使ってないコードを 効率よく消す方法 直接速度に影響するほど大量でなければ、そこまで 大きく変わらないが 速度以外の側面でも無駄なコード少ないに越したこ とはない 46 / 78
Slide 47
Slide 47 text
使ってないコードを 効率よく消す方法 最近はIntelliJ IDEAが賢いのでそれ使うとわり と見つかる scalaのcompiler option指定でも 47 / 78
Slide 48
Slide 48 text
IntelliJ IDEAの使ってない コード検知機能 そのproject内部でグローバルに検知してくれる 多少誤検知ある companion objectにimplicitでinstance定義 したときにobjectが使ってない判定される 48 / 78
Slide 49
Slide 49 text
使ってないコード効率よく消す 自作ツール例 https://xuwei- k.hatenablog.com/entry/2023/02/08/115510 https://github.com/xuwei-k/unused-code https://xuwei- k.hatenablog.com/entry/2022/02/14/152706 49 / 78
Slide 50
Slide 50 text
compile速度 macroが必ず遅くなるわけではないが、結果的に macroが原因なことは多い compiler pluginも種類によっては注意 circeのautoのような再帰的に導出するものは、裏 で重複して展開される可能性が高まるって爆発する ので絶対ダメ 50 / 78
Slide 51
Slide 51 text
compile速度のプロファイル https://github.com/scala/scala/pull/7364 Scala 2のChrome traceを1度は実行してみまし ょう -Yprofile-trace 51 / 78
Slide 52
Slide 52 text
compile速度のプロファイル Scala 3にも入る? https://github.com/scala/scala3/pull/19897 52 / 78
Slide 53
Slide 53 text
Scala 3のinlineでの compile速度爆発との戦い 前にblog書いた https://xuwei- k.hatenablog.com/entry/2022/03/30/142529 53 / 78
Slide 54
Slide 54 text
compile速度のプロファイル Scala 3用で独自に作ったが、公式に前述のものが 入れば必要なくなりそう https://xuwei- k.hatenablog.com/entry/2022/11/12/160933 https://github.com/xuwei- k/scala3profile 54 / 78
Slide 55
Slide 55 text
compileとtestでJVMの profile 個人的によく使ってる https://visualvm.github.io/ その他JVMには色々あるので使えるものはなんでも 使おう 55 / 78
Slide 56
Slide 56 text
testでJVMのprofile 余計なスレッドや、スレッドプール、コネクション プールが作られ過ぎていないか? ActorSystem気軽に作ると結構無駄なので注意 56 / 78
Slide 57
Slide 57 text
testでのprofile CIでは、JVMとその他含めてメモリが効率的に使え ているか? 意味なくメモリなどを使い過ぎているプロセスはな いか? JVMには色々なツールが揃ってるのでフル活用 57 / 78
Slide 58
Slide 58 text
CI上での計測 GitHub Actionsだと、例えばこういうものも https://github.com/catchpoint/workflow- telemetry-action ただし、細かい部分不満がある、一部正しく取れな い 58 / 78
Slide 59
Slide 59 text
CIでのキャッシュ効率 独自にS3保存などの方がいい場合も? https://synamon.hatenablog.com/entry/2022/11 keyの設計を必ずしっかり https://xuwei- k.hatenablog.com/entry/2020/03/16/005728 59 / 78
Slide 60
Slide 60 text
CIでのキャッシュ効率 GitHub Actions公式の制約に注意 容量の制約 default branchで保存しないと他で使えない しっかりキャッシュがヒットしてるか定期的にログ などで確認 60 / 78
Slide 61
Slide 61 text
CIで並列job お金で時間を買う方法 効率は犠牲になるが、ある程度までなら簡単に速く なるので、人間が工夫を頑張る工数は節約 テスト名のhashでやるなど https://labs.septeni.co.jp/entry/2018/11/23/ 61 / 78
Slide 62
Slide 62 text
CIで並列job compileは1つでその後testなどは分岐? その場合にsbtのremote cacheではなく workspace丸ごとuploadしたほうが速い、といっ たノウハウ あるいは、相対的にcompileのコストが低いなら、 最初から富豪的にcompileも重複して実行? 62 / 78
Slide 63
Slide 63 text
sbt-projectmatrix の使用検討 https://github.com/sbt/sbt- projectmatrix デフォルトではsbtは "++ 3.x" などでscala versionを切り替えるが、これは状態を変更してい る 状態を変更しないと他のversionでbuildが出来な いのはデメリット 63 / 78
Slide 64
Slide 64 text
sbt-projectmatrix の使用検討 Scalaのversion以外でも他にも色々な意味で cross buildしたい場合 sbt 2ではこれがデフォルトになる? 普通に設定したら並列にできないものまで並列で可 能になるので 64 / 78
Slide 65
Slide 65 text
sbt-projectmatrix の使用検討 全体としての効率重視したい場合? CIのjob分けるより、効率はいいはず? 65 / 78
Slide 66
Slide 66 text
sbtの concurrentRestrictions 以前のテストの並列の話のblogにも書いたが デフォルトのままではなく細かく変えた方がいい場 合は多々ある 66 / 78
Slide 67
Slide 67 text
個々のtest時間を集計してみる 例えばscalatestだと -oD のようなオプションが あります それをさらにいい感じに集計しないと・・・ これだけでボトルネックわかるか?というと 67 / 78
Slide 68
Slide 68 text
pipelineの有効化検討 ThisBuild / usePipelining := true Scala 2にも3にもある 3に入ったのは最近(2024年4月) https://github.com/scala/scala3/pull/18880 68 / 78
Slide 69
Slide 69 text
pipelineの有効化検討 今回のScala祭で話がある? 普通のcompile option設定ではなく、build toolと連携した特別な設定が必要 69 / 78
Slide 70
Slide 70 text
pipelineの有効化検討 最大、数割程度速くなる可能性? まだそこまで広く使われてないので、罠にハマる可 能性もありそう 70 / 78
Slide 71
Slide 71 text
buildファイルの Scala書き過ぎない sbtが多少?好きな人以外は、そもそもそこを頑張 らないと思うが 使わない部分も必ずcompileされるので sbtとしてのbuildファイルに書く必然性がないも のは他の場所に 71 / 78
Slide 72
Slide 72 text
使っているsbt plugin見直す 稀にsbt pluginせいで遅いことがある。あった ほぼ使ってないものや、メンテされてない古いもの は場合によって脱却しよう maven centralではなく古い非推奨なrepo.scala- sbt.orgのresolverに置いてあるものも注意 72 / 78
Slide 73
Slide 73 text
テストの依存の工夫 https://xuwei- k.hatenablog.com/entry/2022/10/09/124418 73 / 78
Slide 74
Slide 74 text
数日前書いた記事 Scalaやsbt直接関係ない build時間の統計取ったり様々な角度から https://xuwei- k.hatenablog.com/entry/2024/06/07/074942 74 / 78
Slide 75
Slide 75 text
まとめ Scalaのcompileそのものが原因で遅いことはない とは言い切れないが、そこだけがボトルネックにな るのは非常に稀です 仮にcompileが遅くても、CI全体で見るとtest時 間も結構な割合を占めるはずで、そこはあまり Scala特有ではない 75 / 78
Slide 76
Slide 76 text
まとめ 色々やって、それでももっと速くしたい場合は、最 終的にはお金でスケールする余地を残して、作って るプロダクトがCI程度のお金が大したことないほど 儲ければ問題ないのでは? 76 / 78
Slide 77
Slide 77 text
まとめ 今回紹介したような手法ほぼ全て試してもまだ圧倒 的に遅いと感じるならば、他の言語やbuild tool を検討した方がいい可能性はある? 思っている以上に手段は数多くあるので、目的に応 じて適度に頑張りましょう 77 / 78
Slide 78
Slide 78 text
おわり 78 / 78