Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fundsのコンパイル速度を改善した話

taketora
September 15, 2020

 Fundsのコンパイル速度を改善した話

【オンライン開催】09/15(火) Scala関西勉強会 - 2020年09月版

https://connpass.com/event/184638/

サービスが成長するとコードが増えて、コンパイル速度に時間がかかることもあるかと思いますが、FundsのサーバーサイドのScalaプロジェクトのコンパイル速度を44%短縮しました。
コンパイル速度が遅くなる要因や、改善していったプロセスなど発表します。

taketora

September 15, 2020
Tweet

More Decks by taketora

Other Decks in Technology

Transcript

  1. 資産運用スタートアップの開発で採用した PlayによるClean Architectureでの 設計・開発事例 株式会社クラウドポート 若松 慶信 @Scala 関西 Summit

    2019 資産運用スタートアップの開発で採用した、 PlayによるClean Arcitectureでの設計・開発事例 より引用
  2. 2019年11月時点のバックエンド(Funds)のコンパイル速度 [info] Compiling 1348 Scala sources and 4 Java sources

    to  target/scala-2.12/classes ... [info] Done compiling. [success] Total time: 386 s, completed 2019/11/27 20:47:16 6分以上時間がかかっていた 前提条件 • sbtにはメモリ4GBを割り当て • sbtを起動しclean、compileを行った2度目の結果 • 依存性のライブラリはダウンロード済みの状態 環境 • Scala 2.12 • Java 1.8
  3. ちなみに2020年6月時点 検証する前にコンパイル速度が大幅に改善していた $ compile [info] Compiling 1353 Scala sources and

    4 Java sources to target/scala-2.12/classes ... [info] Done compiling. [success] Total time: 133 s, completed 2020/06/02 6:31:33 386 secから133 sec に改善されていた。
  4. 考えられる要因 • Javaのバージョンアップ ◦ Java1.8 → Java11 • Play Framework

    ◦ 2.6.9 → 2.6.25 • 不要になった機能のソースコードの削除 コンパイルの改善にバージョンアップが重要
  5. $ compile [info] Compiling 1353 Scala sources and 4 Java

    sources to target/scala-2.12/classes ... [info] Done compiling. [success] Total time: 133 s, completed 2020/06/02 6:31:33 元々シングルプロジェクトだったので、どのレイヤーでどれだけ時間が かかっているか把握できなかった。 1. マルチプロジェクト化
  6. システムアーキテクチャ Backend Service (Scala) DB, Datastore, External REST Services, etc.

    Service frontend Admin frontend REST API REST API 共通バックエンド サービス ドメインが複雑・密接に関係するバックエンドは モノリシックなサービスで構成 資産運用スタートアップの開発で採用した、 PlayによるClean Arcitectureでの設計・開発事例より引用
  7. Clean Architectureで定義されるレイヤー レイヤー レイヤーの概要 Entity (Enterprise Business Rules) 企業全体のビジネスルール Use

    Case (Application Business Rules) アプリケーション固有のルール・ロジック Interface Adapters 入出力・永続化・DB非依存のSQLなど Frameworks & Drivers フレームワークやドライバなど 資産運用スタートアップの開発で採用した、 PlayによるClean Arcitectureでの設計・開発事例より引用
  8. Fundsで採用しているレイヤー構造 レイヤー レイヤの用途 Domain ドメイン固有の概念・ビジネスロジック Application アプリケーション固有のロジック Interface 入出力やその表現 Infrastructure

    副作用を持つ実装 (DB, HTTP client, 外部サービス, etc.) 資産運用スタートアップの開発で採用した、 PlayによるClean Arcitectureでの設計・開発事例より引用
  9. > compile [info] Compiling 13 Scala sources to library/target/scala-2.12/classes ...

    [info] Done compiling. [info] Compiling 380 Scala sources to domain/target/scala-2.12/classes ... [info] Done compiling. [info] Compiling 288 Scala sources to application/target/scala-2.12/classes ... [info] Done compiling. [info] Compiling 317 Scala sources and 4 Java sources to interfaces/target/scala-2.12/classes ... [info] Done compiling. [info] Compiling 346 Scala sources to infrastructure/target/scala-2.12/classes ... [info] Done compiling. [info] Compiling 9 Scala sources to funds/target/scala-2.12/classes ... [info] Done compiling. [success] Total time: 136 s, completed 1. マルチプロジェクト化
  10. レイヤー compile time(sec) library 3.17 domain 11.2 application 12.74 interfaces

    14.49 infrastructure 85.72 funds 8.61 total 136 • コンパイル速度は変化なし • infrastructureで時間がかかっている ことが可視化 • 開発時に局所的にcompile可能 • build時間が短縮された (jarファイルを生成する時間) ◦ 735sec → 489sec 1. マルチプロジェクト化
  11. SPEEDING UP COMPILATION TIME WITH SCALAC-PROFILINGより引用 The report suggests that

    about 84.3% of the compilation time is spent on typer. This is an unusual high value. Typechecking a normal project is expected to take around 50-70% of the whole compilation time.
  12. typerフェーズでやっていること • 型を推論 • 型が一致するか確認 • 暗黙のパラメータ(implicit parameter)を探索し、構文木に追加 • 暗黙の変換(implicit

    conversion)を行う • オーバーロードを解決 • 親の参照型をチェック • implicit探索 • マクロ展開 • ケースクラスの追加メソッドを作成(copy,applyなど) Scala Compiler Phases with Picturesより
  13. Infrastructureの実装例: Slick trait CustomerTable extends ColumnMappers { this: Profile =>

    import profile.api._ class Customers(tag: Tag) extends Table[CustomerRow](tag, "customer") { def id = column[ID[Customer]]("id", O.SqlType("INT"), O.PrimaryKey, O.AutoInc) def email = column[String]("email", O.SqlType("VARCHAR(256)")) def status = column[CustomerStatus]("status", O.SqlType("INT")) ... type TableRecord = (Option[ID[Customer]], String, CustomerStatus) def * = (id.?, email, status) <> ( (t: TableRecord) => { val (id, email, status_) = t CustomerRow(id, email, status) }, (customer: CustomerRow) => { Option((customer.id, customer.email, customer.status)) } ) } } 資産運用スタートアップの開発で採用した、 PlayによるClean Arcitectureでの設計・開発事例より引用
  14. 回数 既存 MacWire 1 162 169 2 160 140 3

    137 138 4 127 151 5 130 132 6 131 136 7 139 134 8 124 140 9 131 138 10 132 133 average 137.3 141.1 • 約500の暗黙のパラメータを削減 • コンパイル速度は変化なし 2. MacWireによるDIへ変更
  15. 3. Implicit Conversion の定義ファイルを分割 回数 既存 ファイル分割 1 162 80

    2 160 75 3 137 75 4 127 84 5 130 80 average 143.2 (137.3) 78.8 コンパイルが遅くなっている原因は必要のない暗黙の変換を130ものtraitに ミックスインしていることだった
  16. 回数 既存 マルチ プロジェクト MacWire 分割 1 162 145 155

    80 2 160 121 133 75 3 137 119 140 75 4 127 113 151 84 5 130 120 132 80 average (sec) 143.2 (137.3) 123.6 142.2 78.8 今回の検証の結果
  17. typerフェーズでやっていること • 型を推論 • 型が一致するか確認 • 暗黙のパラメータ(implicit parameter)を探索し、構文木に追加 • 暗黙の変換(implicit

    conversion)を行う • オーバーロードを解決 • 親の参照型をチェック • implicit探索 • マクロ展開 • ケースクラスの追加メソッドを作成(copy,applyなど) Scala Compiler Phases with Picturesより
  18. 検証の結果より • 暗黙のパラメータはコンパイル速度にあまり影響がなかった。 • 一つのファイルに約250のimplicit conversionを定義して、 約130のトレイトに ミックスインしていた箇所でcompile 時間が増大していた。 •

    マルチプロジェクトにするとビルドの速度が向上した。 Fundsのケースでは、implicit conversionはまとめて定義するのではなく分割して 定義し、不要なimplicit conversionの読み込みを減らすことで コンパイル速度の向上した。
  19. 4. Compile時にGraalを使う 回数 既存 GraalVM 1 162 198 2 160

    181 3 137 190 4 127 179 5 130 185 6 131 171 7 139 169 8 124 165 9 131 164 10 132 169 average 137.3 177.1 遅くなってしまったので不採用 早くなる事例があるので、実行する環境に依存 するのではないかと推測しています 検証した環境 • Scala 2.12.10 • Java 11.0.7