Slide 1

Slide 1 text

Optimising Scala 3 Build times 2024 edition ● Twitter: @bishabosha ● Email: [email protected] Jamie Thompson Scala 3 ビルド時間を最適化 1

Slide 2

Slide 2 text

Agenda Scala はどのようにコードをビルドするか、去年から 何が変わったか、また今後の展望について 2 Scala build 101 Changes 2023-2024 Future Speculation What happens when you build code? Concrete steps we made to improve performance What can we do next?

Slide 3

Slide 3 text

Research Open Source 8000+ Github repos 1,200,000+ artifacts Industry 非営利団体として、OSSコミュニティと業界と 学会の橋渡しをしています 3 Scala Native Scala.js Dotty (Scala 3) Caprese (new effect system)

Slide 4

Slide 4 text

たくさんのプロジェクトに関わりましたが、私たちの焦点は開発 者の作業をより楽にする開発ツールにあります 4 2 Debug Adapter LSP server for Scala Compile Scala to JS Index of Scala libraries Online playground for Scala Porting to Scala 3 expression evaluator, smart stack trace, hot code swapping Scala.js

Slide 5

Slide 5 text

Corporate Membership https://scala.epfl.ch 会社との提携や個人的な寄付を歓迎します。これによりもっと エキサイティングなプロジェクトを実現できます 5

Slide 6

Slide 6 text

Explaining the Scala Build “Premature optimisation is the root of all evil” - Donald Knuth Scalaビルドについて話しましょう。ツールの相互作用と性能最 適化のボトルネックについて話します 6

Slide 7

Slide 7 text

Build Tools 現在、様々なビルドツールがScalaプロジェクトを 扱うことができます 7 Scala CLI

Slide 8

Slide 8 text

Build Tools この中の大部分は、同じツールチェーンに基づいてScalaコー ドをビルドしています。詳しく見てみましょう 8 Scala CLI

Slide 9

Slide 9 text

Build Tools このシステムは、三つの抽象層で構成していると 考えられます 9 Zinc scalac

Slide 10

Slide 10 text

Build Tools 第一層はビルドツール、例えば sbt、mill、gradle、scala-cli。高 いレベルでプロジェクトを管理しています 10 Zinc scalac Layer 1

Slide 11

Slide 11 text

Build Tools 第二層はZinc。コンパイラをラップして、ビルドツールに統一さ れたインターフェースを提供します 11 Zinc scalac Layer 1 Layer 2

Slide 12

Slide 12 text

Build Tools 第三層はコンパイラ自身です 12 Zinc scalac Layer 1 Layer 2 Layer 3

Slide 13

Slide 13 text

ビルドツールはプロジェクトの基本情報と Mavenアーティファクトの管理 13 Layer 1 common server client name = “common” libs = [“com.lihaoyi::os-lib:0.1.0”] srcs = “common/**/*.scala” gens = “gen-protobuf” name = “server” deps = [“common”] srcs = “server/**/*.scala” name = “client” deps = [“common”] srcs = “client/**/*.scala” Build Tools - Layer 1

Slide 14

Slide 14 text

実際のファイルを用意して(ダウンロードか生成)、 次の層に渡します 14 C.scala E.scala F.scala A.scala B.scala os-lib.jar fetch “com.lihaoyi::os-lib:0.1.0” require “common” to build first require “common” to build first Layer 1 generate sources from protobuf schema Build Tools - Layer 1

Slide 15

Slide 15 text

プロジェクト間の依存関係は DAG を形成します 15 server database auth API common client Build Tools - Layer 1 Layer 1

Slide 16

Slide 16 text

server common auth API common database 赤いプロジェクトはお互いに依存していないため、 並行してコンパイルできます 16 client Build Tools - Layer 1 Layer 1

Slide 17

Slide 17 text

server common auth API common database 何故この順序でコンパイルされなければ いけないのでしょうか 17 client Build Tools - Layer 1 Layer 1

Slide 18

Slide 18 text

Layer 1 API common APIはcommonが生成する JAR が必要で、clientはcommon とAPIが生成する JAR が必要だからです。 18 client Inputs: [“common/**/*.scala”] Outputs: [common.jar] Inputs: [“api/**/*.scala”,common.jar] Outputs: [api.jar] Inputs: [“client/**/*.scala”,common.jar,api.jar] Outputs: [client.jar] Build Tools - Layer 1

Slide 19

Slide 19 text

Layer 1 コンパイル・タスクの仕組みを API プロジェクトを 例に解説します。 19 Inputs: [“api/**/*.scala”,common.jar] Outputs: [api.jar] Build Tools - Layer 1 API

Slide 20

Slide 20 text

Build Tools - Layer 2 第二層のZincを見てみましょう 20 Zinc scalac Layer 1 Layer 2

Slide 21

Slide 21 text

コンパイラを抽象化して、仮想ファイルに対する 差分コンパイル・サービスを提供します 21 Layer 2 Zinc ● Virtual file system ● Tracks changes in sources ● Compiles only necessary files A.scala B.scala machine-independent paths Build Tools - Layer 2 ● Cached compiler ● Cancellation

Slide 22

Slide 22 text

前のコンパイルと比べて、 変更があったソースを無効化します 22 Layer 2 Zinc Build Tools - Layer 2 A.scala B.scala C.scala D.scala E.scala F.scala G.scala H.scala New compilation request, invalidate sources.

Slide 23

Slide 23 text

A.scala に変更があったことを検知したとします 23 Layer 2 Zinc Build Tools - Layer 2 A.scala B.scala C.scala D.scala E.scala F.scala G.scala H.scala Detect A.scala has changed, compile it.

Slide 24

Slide 24 text

A.scalaと依存関係あるファイルもチェック 24 Layer 2 Zinc Build Tools - Layer 2 A.scala B.scala C.scala E.scala G.scala H.scala D.scala F.scala invalidate any dependencies affected by changes, e.g. F.scala and D.scala.

Slide 25

Slide 25 text

必要なファイルだけコンパイルして、時間を節約します 25 Layer 2 Zinc Build Tools - Layer 2 A.scala B.scala C.scala E.scala G.scala H.scala D.scala F.scala No more changes. We are done!

Slide 26

Slide 26 text

詳しい説明はScala Days Seattle 2023の トークをご覧ください。 26 Layer 2 Zinc Build Tools - Layer 2 A.scala B.scala C.scala E.scala G.scala H.scala D.scala F.scala watch my previous talk for more info! Scala Days Seattle 2023: How Does Incremental compilation work with Scala 3? (YouTube)

Slide 27

Slide 27 text

Build Tools - Layer 3 第三層のコンパイラを見てみましょう 27 Zinc scalac Layer 1 Layer 2 Layer 3

Slide 28

Slide 28 text

Build Tools - Layer 3 ソースファイルを読み込んで、クラス、tasty、 およびjarファイルなどを生成します。 28 Layer 3 Zinc scalac api.jar A.class B.class C.tasty A.scala B.scala C.scala common.jar -classpath \ scala3-library_3.jar API

Slide 29

Slide 29 text

コンパイルラン自体は、 複数のフェーズから構成されています 29 Layer 3 Zinc scalac Build Tools - Layer 3 Run Phases genBCode pickler parser deps api typer

Slide 30

Slide 30 text

parserフェーズは、ソースファイルのテキストを 読み取り、それを抽象構文木に変換します 30 Layer 3 Zinc scalac genBCode pickler parser deps api typer Build Tools - Layer 3 read source files, check syntax

Slide 31

Slide 31 text

typerフェーズは、型推論、implicitの検索、 オーバーロードのチェックなどを実行します 31 Layer 3 Zinc scalac Build Tools - Layer 3 genBCode pickler parser deps api typer is the program valid?

Slide 32

Slide 32 text

依存関係フェーズは、コンパイル外部の シンボルの使用を分析します 32 Layer 3 Zinc scalac genBCode pickler parser deps api typer Build Tools - Layer 3 What symbols were used?

Slide 33

Slide 33 text

picklerフェーズは、TASTyファイルを生成します 33 Layer 3 Zinc scalac genBCode pickler parser deps api typer Build Tools - Layer 3 produce TASTy files

Slide 34

Slide 34 text

APIフェーズは、定義のシグネチャの変動を認識し、 incrementalコンパイルに使わせます 34 Layer 3 Zinc scalac genBCode pickler parser deps api typer Build Tools - Layer 3 record API signatures

Slide 35

Slide 35 text

最後のフェーズは、クラスファイルを生成します 35 Layer 3 Zinc scalac genBCode pickler parser deps api typer Build Tools - Layer 3 produce class files

Slide 36

Slide 36 text

Build Tools - Advice Tip No. 1 Use small files! A more granular dependency graph avoids unnecessary recompilation 小さいファイルを使えば、 コンパイルのスピードが速くなります 36

Slide 37

Slide 37 text

Tip No. 2 With a more granular project graph, you can introduce parallelism. Split apps into smaller projects! 複数のサブプロジェクトに分割すると、 並行コンパイルの可能性が増えます 37 Build Tools - Advice

Slide 38

Slide 38 text

Tip No. 3 Thread starvation, duplicate work Don’t make projects too small! 非常に小さなモジュールは競合の増加や作業の重複などに よってパフォーマンスを低下させる可能性があります。 38 Build Tools - Advice

Slide 39

Slide 39 text

Performance updates 2023 - 2024 the road to pipelined builds and beyond. 過去1年間のコンパイラの変更点で パフォーマンス関連のものを総括しましょう 39

Slide 40

Slide 40 text

Remote Build Cache sbtのリモートビルドキャッシュを Scala 3.4.0でサポートできました Scala 3.4.0 40

Slide 41

Slide 41 text

Remote Build Cache 大規模プロジェクトの再構築を避けた実績があります 41

Slide 42

Slide 42 text

Progress Tracking and Cancel 進捗状況の追跡とキャンセルも可能になりました 42 Scala 3.4.0 not frozen at 0% !

Slide 43

Slide 43 text

Progress Tracking and Cancel 進捗情報は偽物ではありません 43

Slide 44

Slide 44 text

Progress Tracking and Cancel ソースファイルの数とフェーズの積で総進捗を計算します 44 total = |files| × |phases| current = |files| × |seen_phases| + |seen_in_phase| progress = current / total refreshed after every compilation unit and phase genBCode pickler parser deps api typer

Slide 45

Slide 45 text

Progress Tracking and Cancel コンパイラの各時点の進捗はこのようになります 45 genBCode pickler parser deps api typer files = [A.scala, B.scala, C.scala, D.scala] 3% 6% 17% 30% 33% 100%

Slide 46

Slide 46 text

Progress Tracking and Cancel Zincのキャンセル機能と連携し、進捗を更新する時点で 中断することができます 46 files = [A.scala, B.scala, C.scala, D.scala] cancel compilation genBCode pickler parser deps api typer

Slide 47

Slide 47 text

Pipelined Builds Scala 3.5でsbtのビルドパイプライニングも サポートできました Scala 3.5.0 47

Slide 48

Slide 48 text

Project B Project A Project D off pipelining Pipelined Builds パイプライニングを導入してない場合、依存関係あるプロジェク トは前のプロジェクトを待たないといけません 48

Slide 49

Slide 49 text

Project B Project D saved time! on pipelining Project A Pipelined Builds パイプライニングを導入した場合、依存関係があっても 次のプロジェクトを早く開始することが可能です 49 *projects and dependencies should not define macros ThisBuild / usePipelining := true

Slide 50

Slide 50 text

Pipelined Builds パイプライニングがない場合、クラスとTASTyファイルは最後の api.jarで一緒に提供する 50 API A.scala B.scala C.scala common.jar api.jar off pipelining genBCode pickler parser deps api typer - Class files - TASTy files

Slide 51

Slide 51 text

pipelining on Pipelined Builds パイプライニングがある場合、TASTyファイルだけを先に picklerのフェーズで提供できます 51 API A.scala B.scala C.scala common.jar model.jar genBCode pickler parser deps api typer model- EARLY.jar TASTy files only

Slide 52

Slide 52 text

data server infra template webclient coreJS coreJVM Scaladex project layout Pipelined Builds - example プロジェクトのレイアウトは、パイプライン化の程度に 影響します。Scaladexはいいレイアウトの手本です。 52

Slide 53

Slide 53 text

BEFORE パイプライニングの実際節約した時間は プロファイリングデータで見られます 53 pipelining on saved time! AFTER off pipelining

Slide 54

Slide 54 text

Pipelined Builds macrosがある場合、クラスファイルが必要なので、 パイプライニングは使えません 54 A Warning Turn off pipelining for macros and their dependencies!

Slide 55

Slide 55 text

-- Error: /example/src/main/scala/example.scala:15:52 15 | val span: Binding.Stable[HTMLSpanElement] = 16 | html"foo" | ^^^^^^^^^^^^^^^^^^^^^^ |Macro code depends on missing object Definitions found on the classpath, but could not be | loaded while evaluating the macro. This is likely because class files could not be | found in the classpath entry for the symbol. | | A possible cause is if the origin of this symbol was built with pipelined compilation; | in which case, this problem may go away by disabling pipelining for that origin. | |object Definitions is defined in file | /definitions/target/early/definitions-early.jar(Definitions.tasty) |--------------------------------------------------------------------------- |Inline stack trace |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from package.scala:712 712| ${ Macros.html('stringContext, 'args) } --------------------------------------------------------------------------- Pipelined Builds macrosとパイプライニングが衝突する場合、 エラーメッセージでヒントを与えます 55

Slide 56

Slide 56 text

Scala 3のパイプライニング機能はリリースまで 一年もかかりました 56 timeline 2023 - 2024 Jun 06 2023 Demo pipeline and outline builds at Scala Days (???)

Slide 57

Slide 57 text

TASTyファイルのみのクラスパスがサポートできました 57 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0)

Slide 58

Slide 58 text

ビルドキャッシュがサポートできました 58 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3)

Slide 59

Slide 59 text

進捗状況の追跡がサポートできました 59 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0)

Slide 60

Slide 60 text

JavaファイルのTASTyファイルもサポートできました。 これは一番時間をかけたマイルストーンです。 60 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Nov 28 2023 Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0) Write Java Definitions to TASTy (Scala 3.4.0)

Slide 61

Slide 61 text

Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Nov 28 2023 Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0) Write Java Definitions to TASTy (Scala 3.4.0) timeline 2023 - 2024 // A.tasty Trees (70 bytes, starting from 153): 44: TYPEDEF(24) 2 [A] 47: TEMPLATE(21) 49: SHAREDtype 23 51: SPLITCLAUSE 52: ... 61: DEFDEF(7) 12 [foo] 64: EMPTYCLAUSE 65: SHAREDtype 38 67: ELIDED 68: SHAREDtype 38 Attributes (4 bytes, starting from 287): JAVAattr OUTLINEattr SOURCEFILEattr 14 [A.java] JavaファイルのTASTyファイルもサポートできました。 これは一番時間をかけたマイルストーンです。 61 // A.java public class A { public void foo() { System.out.println("Hello"); } }

Slide 62

Slide 62 text

Scala 2の「-Ytasty-reader」がサポートできました 62 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Nov 28 2023 Feb 20 2024 Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0) Write Java Definitions to TASTy (Scala 3.4.0) support pipeline TASTy with -Ytasty-reader (Scala 2.13.13)

Slide 63

Slide 63 text

Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0) Write Java Definitions to TASTy (Scala 3.4.0) support pipeline TASTy with -Ytasty-reader (Scala 2.13.13) Java TASTy feature complete (Scala 3.4.0) Scala 3.4でJavaのTASTyモードを一緒に リリースしました 63 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Nov 28 2023 Feb 20 2024 Feb 22 2024

Slide 64

Slide 64 text

Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0) Write Java Definitions to TASTy (Scala 3.4.0) support pipeline TASTy with -Ytasty-reader (Scala 2.13.13) Java TASTy feature complete (Scala 3.4.0) Pipelining complete (Scala 3.5.0) Scala 3.5でパイプライニング機能が完成しました 64 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Nov 28 2023 Feb 20 2024 Feb 22 2024 Apr 04 2024

Slide 65

Slide 65 text

Demo pipeline and outline builds at Scala Days (???) Support TASTy only classpaths (Scala 3.4.0) Support Zinc 1.4+ (sbt Remote Build Cache) (Scala 3.3.3) Compile progress tracking (Scala 3.4.0) Write Java Definitions to TASTy (Scala 3.4.0) support pipeline TASTy with -Ytasty-reader (Scala 2.13.13) Java TASTy feature complete (Scala 3.4.0) Pipelining complete (Scala 3.5.0) Write TASTy in parallel (Scala 3.5.0) 追加機能として、TASTyを書き込んでる時、 メインスレッドをブロックしないようになりました 65 timeline 2023 - 2024 Jun 06 2023 Jul 03 2023 Jul 23 2023 Oct 31 2023 Nov 28 2023 Feb 20 2024 Feb 22 2024 Apr 04 2024 Apr 16 2024

Slide 66

Slide 66 text

Future Updates and changes what can we do next? 未来リリース予定の新機能について説明します 66

Slide 67

Slide 67 text

Parallel “Semantic” extractors 別のスレッドでTASTyからSemanticDBとZincの メタデータを抽出することで加速します。 67 genBCode pickler parser deps api typer before genBCode pickler parser typer after deps api tasty SAVED TIME ! semanticdb

Slide 68

Slide 68 text

アウトラインタイプチェック機能も研究しています 68 pass full Multi-pass compilation lowering, erasure backend deps, api, pickler type checking pipelining start off outline typer

Slide 69

Slide 69 text

定義の本体ではなくシグネチャのみタイプすると、パイプライニ ングの開始できる時点が更に早くなります 69 pass 1 lowering, erasure backend on Multi-pass compilation pipelining start (full compile) outline type checking api, pickler outline typer

Slide 70

Slide 70 text

ファイルをバッチに分割し、複数のスレッドで コンパイル時間を短くすることもできます 70 pass 2 pass 1 lowering, erasure backend on Multi-pass compilation outline type checking api, pickler outline typer pipelining start (full compile) saved time!

Slide 71

Slide 71 text

Batch Compilation 単一のパスの場合、他のファイルのアウトラインタイプを利用 し、加速することもできます 71 outline typer D.scala B.scala E.scala B.scala A.scala D.scala E.scala batch A. batch B. on off outline typer pass full batch A. pass full batch B. saved time maybe? A.scala

Slide 72

Slide 72 text

Benchmarks Enough! show me the numbers! ベンチマークデータを見てみましょう 72

Slide 73

Slide 73 text

Testing on MacBook Pro 14” 2021 (M1 Pro, 32GB RAM) From cold sbt start: - clean; compile 2x to warm up - then take mean time of next 7 cycles. Benchmarks - pipelining M1 Pro CPU、32ギガバイトのRAMを搭載した MacBook Pro 14インチで記録されました 73

Slide 74

Slide 74 text

“clean compile” time & memory 308,829 LOC lichess-org/lila The key takeaway seems to be that you trade time overall for peak memory. 72s 6GB 55s 8.3GB 3.3.0 (standard) 3.3.2-SNAPSHOT (pipelined) 5600 lines/s Benchmarks - pipelining パイプライニングのベンチマーク 74

Slide 75

Slide 75 text

Benchmarks - pipelining Scaladex 31% improved 3.3.2-SNAPSHOT (pipelined) Time to finish “clean compile” time Your mileage may vary Other projects パイプライニングのベンチマーク 75

Slide 76

Slide 76 text

Benchmarks - outlining 158,000 LOC 30s 1.54x faster! we could still do better… 19.5s 3.3.2-SNAPSHOT (2-pass, 3 workers) 3.3.1 (single pass) 8119 lines/s 5277 lines/s lampepfl/dotty アウトラインコンパイルのベンチマーク 76

Slide 77

Slide 77 text

Demo デモを見てみましょう 77