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

並列度を下げたら速くなった — go build の重複コンパイルを実測で見る

Avatar for Tomoaki Tomoaki
November 09, 2026

並列度を下げたら速くなった — go build の重複コンパイルを実測で見る

バクラクの monorepo で、同時に走らせる go build を 9 → 2 に減らしたら、起動時のビルドが 2.4 倍速くなりました。-debug-actiongraph でビルドの中を覗き、重複コンパイルが起きる理由と、さらに速くする方法を実測で紹介します。

layerx.go #6(2026-11-09)

Avatar for Tomoaki

Tomoaki

November 09, 2026

More Decks by Tomoaki

Other Decks in Technology

Transcript

  1. 自己紹介 Tomoaki Kitaoka LayerX (2019-08 ~ ) バクラク事業部 ソフトウェアエンジニア AI-OCR・AI

    仕訳などの AI 機能開発 2025 年から AI-BPO 事業の立ち上げ X: @tapioca_pudd 趣味はクロスフィット © LayerX Inc. 2
  2. 背景 バクラクの monorepo と lxpm バクラクは複数のプロダクトを 1 つの monorepo で開発している

    Go のサービス 約 130 個 lxpm が扱うサービスは約 200 個。うち go build して 起動する Go のサービスが 約 130 個 lxpm: ローカル開発用のプロセスマネージャ 全部は起動せず、開いたプロダクトが使うサービスだけを、 依存先も含めてその場でビルドして起動する 参考: lxpm について詳しくは layerx.go #5 の発表へ speakerdeck.com/izumin5210/layerx-go-5 © LayerX Inc. lxpm がビルドして起動する分 開くプロダクトによって変わる(だいたい 10〜12 個) 3
  3. 背景 lxpm は、必要になったサービスだけをその場で go build する 以前は Go のサービスを 1

    つのバイナリにまとめて いたが、1 行の変更でも全部ビルドし直しに なり遅かった lxpm はポートを先に全部確保しておき、 リクエストが来たサービスだけを go build して 起動する サービスをまたぐと「呼ばれてからビルド」が順に 積み重なって遅いので、依存先(dependsOn)も 同時にビルドして起動する → この「同時にビルドする数」が、今日の話の 並列度 リクエストが来たサービスから順に building → running になっていく 出典: layerx.go #5「コンパウンドプロダクト開発のためのローカルプロセスマネージャー再発明」 speakerdeck.com/izumin5210/layerx-go-5 © LayerX Inc. 4
  4. 背景 例: バクラク申請・経費精算を開発するとき 申請・経費精算 専用(4 個) バクラク申請・ 経費精算 を開く 申請フロー

    依存を たどる → 稟議・承認 ほか 2 個 複数のプロダクトで共通して使う基盤(8 個) 認証 ID・テナント管理 非同期ジョブ go build → ほか 5 個 12 個の サービスが ローカルで 起動 起動するサービスの数はプロダクトによって変わるが、だいたい 10〜12 個 この 12 個のサービスを「同時に N 個ずつ」go build する。今日の話は、この N をいくつにするか © LayerX Inc. 名前はイメージです 5
  5. 仮説 外側の並列度:同時に動かす go build の数 N 本の枠で go build を同時に走らせ、枠が空いたら次を始める

    並列1 並列2 1 1 2 3 1 並列9 3 4 10 12 2 4 3 5 4 5 10 9 11 12 9 10 12 2 5 9 11 直感的には、並列1・2より9のほうが速い…? © LayerX Inc. 11 数字 = サービス(12 個)· 点線 = 全部終わる時刻 · シェルで書くと … | xargs -P 2 -I{} go build -o bin/ {} 時間→ · lxpm のデフォルトは 18 コアの Mac で 9 だった 7
  6. No でした 増やすほど、起動はむしろ遅くなった 検証した環境 Apple M5 Pro(18 コア / 48GB)·

    go1.27.3 バクラク申請・経費精算の 12 個を空のキャッシュから go build · 並列 1・2・3・4・9 を交互に 3 回ずつ
  7. 先に結論 同時に走らせる go build を 9 → 2 に減らしたら速くなった 原因は

    重複コンパイル。キャッシュはコンパイルが終わった結果しか共有しないので、同時に走る go build が同じ package を それぞれコンパイルする 並列9 完了時間 並列2 完了時間 © LayerX Inc. 290秒 コンパイル回数 コンパイル回数 うち重複 122秒 2,483回(重複394) 4,400回(重複2,311) ×2.4 速くなった 9
  8. go build の仕組み 12 個の go build のうち 1 個の中を覗く

    依存関係の図(action graph)の中から、依存のそろった作業を 個の worker が順に拾っていく -p 最初は全workerが埋まる 動いているのはcompile1個だけ(23秒) linkは1個 共有の依存を⼀⻫にコンパイル ⼤きいpackage1つ(23.6秒)を待つ 1 5.8秒・0.6コア worker 18 18 コア compile1個でも中で並列。それでも18コア中約4しか使わない (推定) 0 0秒 10秒 compile © LayerX Inc. 20秒 link 30秒 使っているコア数 40秒 50秒 60秒 70秒 80秒 完了73.5秒/CPU時間403.4秒→平均5.5コア 12 個のうち 1 個を、空のキャッシュから単独でビルドした記録 · -p はデフォルト値(コア数の 18)· コア数は各作業の CPU 時間から推定 · 出典: [8][13] 11
  9. なぜ遅くなるのか ビルドキャッシュは、コンパイルが終わった結果しか共有しない 1 個ずつ(並列 1) go build A package X

    を compile go build B 同時に 2 個(並列 2) 保存 X はヒット 時間 → go build A X が無い → compile 保存 go build B X が無い → compile 保存 時間 → 別プロセスが コンパイル中 の package を待つ仕組みは無い キャッシュにまだ無ければ、自分でもコンパイルする 同時に走ると、どちらも「まだキャッシュに無い」と見て、両方がコンパイルする 1 個ずつなら、2 個目は 1 個目が保存した結果をそのまま使える Go のソースにも、同じキャッシュを複数のプロセスで使うと “may duplicate effort but will not corrupt the cache” と書かれている cmd/go/internal/cache [7] © LayerX Inc. 出典: go help cache [2]、cmd/go/internal/cache/cache.go [7](付録「参考資料」) 12
  10. なぜ遅くなるのか ビルドする量の半分は、12 個すべてが使う共通 package 12 個のサービスの依存を足し合わせた内訳(2 個のサービスが使う package は 2

    回数える) 共通package×12個のサービス 同時にビルドすると、ここが何度もコンパイルされうる 51% 28% 21% 12個すべてが使う 7〜11個が使う 1〜6個が使う ほかのプロダクトも大体同じ: 全サービスが使う共通 package の割合は、プロダクト A で 48%、プロダクト B で 49% 同時に走る go build は、共通 package を同じ頃にコンパイルしようとする → まだキャッシュに無いので、それぞれが 自分でコンパイルする © LayerX Inc. 13
  11. 実測 同じ package を何回コンパイルしたか 並列 1 並列 2 並列 9

    2,089 回 2,089 重複 0 回 +394 2,089 重複 394 回 +2,311 重複 2,311 回 重複した compile の CPU 時間 並列 2: 144 秒 並列 9: 1,843 秒 並列 9 では、同じ package を最大 9 回 コンパイルしていた 例えば、ある外部ライブラリの package 1 つは 9 回コンパイルされ、コアの取り合いで 1 回に最大 30 秒 かかった(並列 1 では 2.3 秒) © LayerX Inc. 申請・経費精算の 12 個 · 並列ごとに 3 回のうち完了時間が中央値の回の記録(-debug-actiongraph)· Apple M5 Pro 18 コア / 48GB 14
  12. 実測 ビルドを再生する: 12 個の go build の並び 並列 9 は一斉に始まるが、それぞれが遅くなり、全体も遅く終わる(濃い色

    = 一番重い 1 個) 並列1 完了161秒 並列2 完了122秒 並列9 完了290秒 0秒 © LayerX Inc. 60秒 120秒 180秒 240秒 300秒 申請・経費精算の 12 個 · 並列ごとに 3 回のうち完了時間が中央値の回の記録(-debug-actiongraph)· Apple M5 Pro 18 コア / 48GB 15
  13. 実測 ビルドを再生する: 使っているコア数 並列 1 は後半にコアが余る。並列 9 は最初に 100 個以上の作業が同時に走り、使えたコアの多くが

    重複 に回る 並列1 18コア 並列2 18コア 並列9 18コア CPU844秒 重複394回 CPU989秒 重複2,311回 CPU3,215秒 0秒 © LayerX Inc. 重複0回 compile 60秒 重複compile 120秒 link 180秒 240秒 300秒 高さ = 使っているコア数(各作業の CPU 時間から推定。18 で頭打ち)· 申請・経費精算の 12 個 · 3 回のうち完了時間が中央値の回 · Apple M5 Pro 18 コア 16
  14. 実測 結果: 並列 2〜4 が最速帯、9 は 2.4 倍遅い 毎回空のキャッシュから、並列 1・2・3・4・9

    を交互に 3 回ずつ go build(lxpm と同じフラグ) 完了時間 (実際に待った時間) 低いほど速い 290秒×2.38 最速帯 161秒×1.32 127秒×1.04 122秒×1.00 122秒 CPU時間 3,215 (全コアの合計) 827 989 並列1 並列2 並列1でも必要な分 © LayerX Inc. 1,385 1,379 並列3 並列4 並列1より増えた分(≒重複) 並列9 申請・経費精算の 12 個 × 3 回の中央値(ひげは最小〜最大)· 並列 3 は別の回に追加で測定 · Apple M5 Pro 18 コア / 48GB 17
  15. 考察 なぜ 1 でも 3 以上でもなく 2 なのかを考える 並列 1

    は重複がないので、その CPU 時間 827 秒が「ビルドに本当に必要な量」。これを完了時間で割って比べる 827 秒 ÷ 並列 1(重複なし)の CPU 時間 並列 1 並列 2 並列 3 並列 4 並列 9 827 ÷ 161 秒 827 ÷ 122 秒 827 ÷ 127 秒 827 ÷ 122 秒 827 ÷ 290 秒 完了時間 並列度ごとに違う = 1 秒あたりのコア数 重複を除いたビルドに使われた分 重複はないが、待ちの間にコアが空く 重複は増えるが、空いたコアが埋まる これ以上は増えない 同じく増えない かえって減る 5.1 コア 6.8 コア 6.5 コア 6.8 コア 2.9 コア 並列数を上げても、重複を除いたビルドに使われるコアは 6.8 から増えない © LayerX Inc. 申請・経費精算の 12 個 × 3 回の中央値 · 並列 3 は別の回に追加で測定 · Apple M5 Pro 18 コア / 48GB 18
  16. 考察 増やした分の CPU は、重複に回る 完了時間 161秒 122秒 127秒 122秒 290秒

    18コア 平均で使った コア数 =CPU時間÷完了時間 計5.1 計8.1 計10.9 計11.1 40% 増えた分 増えた分 増えた分 6.5 6.8 40% 増えた分16% 計11.3 74% 5.1 6.8 並列1 並列2 並列3 並列4 並列9 重複は0 1個⽬の待ちを埋める 2より少し遅い 完了時間は2と同じ 半分以上が重複 待ちの間にコアが空く 重複はまだ少ない 増えた分は重複 重複を除いたビルドに使われたコア数(並列1のCPU時間827秒÷完了時間) 増えた分は重複 2.9 2の2.4倍遅い 並列1より増えた分(≒重複) 並列 2 で空いたコアはほぼ埋まる。3 個目からは、増えた CPU がほぼ重複に使われる © LayerX Inc. 申請・経費精算の 12 個 × 3 回の中央値 · 並列 3 は別の回に追加で測定 · Apple M5 Pro 18 コア / 48GB 19
  17. 汎用性 monorepo のほかのプロダクトでも、並列 2 ならほぼほぼ最速 18 コアの Mac · 並列

    2 を 1 とした完了時間の比(低いほど速い) プロダクトA プロダクトB 並列2=237秒 並列2=105秒 ×1.52 ×1.46 ×1.02 並列1 ×1.00 並列2 ×0.96 ×1.01 並列3 並列4 ×1.19 並列9 並列1 ×1.00 並列2 ×1.18 ×1.17 並列3 並列4 並列9 B は並列 2 が最速。1・3・4 は約 1.2 倍、9 は 1.5 倍遅い A は 1〜4 がほぼ同じ。一番重い 1 個のビルドだけで 130〜170 秒かかり、全体の時間をほぼ決めている © LayerX Inc. A は 12 個、B は 10 個のサービス · 3 回の中央値 · 空の GOCACHE 20
  18. 汎用性 並列 2 は、ほかのリポジトリでも最適? 仕組みはどこでも同じ。ただし「2」という値は条件で変わる 最適な並列数に大きく影響する要因 要因 依存の共有 コアの数 キャッシュ

    メモリ 最適な並列数が多くなるのは 少ないとき 多いとき 温まっているとき 多いとき 理由 重複が減る 1 個の go build が残す隙間が大きい(8vCPU では 1 ≒ 2) 重複が起きにくい(今回は未計測) 同時に走る compile が増えても足りる 依存を共有する go build を同時に走らせるなら、増やしすぎると重複で遅くなる。最適な並列数そのものは、表の要因で 大きく変わる © LayerX Inc. go build は 1 個でも中で並列 [1]。別々のプロセスは、キャッシュを共有しても作業が重複しうる [7] · 重複は -debug-actiongraph で同じ ActionID の compile を数えれば確かめられる 21
  19. 汎用性 コアが少ない環境(8vCPU)では、並列 1 でも 2 でもほぼ同じ 8vCPU のクラウド上の開発環境 · 並列

    2 を 1 とした完了時間の比(低いほど速い) 申請・経費精算 プロダクトA 並列2=443秒 ×1.85 ×1.32 ×1.01 並列1 並列2=711秒 ×1.00 並列2 ×0.97 並列4 並列8 並列1 ×1.25 ×1.00 並列2 並列4 ×1.45 並列8 並列 1 と 2 はほぼ同じ。コアが少なく、go build 1 個の時点で隙間があまり残らない 並列 2 に比べて、並列 4 で 1.25〜1.32 倍、並列 8 で 1.45〜1.85 倍遅い 参考: リモート開発環境(haro)について tech.layerx.co.jp/entry/why-we-built-our-background-agent © LayerX Inc. 2 回の平均 · 空の GOCACHE 22
  20. 仮説 さらに速くするには? 内部を見ると、まだ余裕がある 申請・経費精算の 12 個 · 並列 2 ·

    空のキャッシュから(完了時間が中央値の回の記録) 18 内部を⾒ると:並列2で使っているコア数(18コア中、平均7.5) 0 0秒 20秒 40秒 60秒 compile 重複compile link まだ空いている(平均10.5コア分) 完了122秒 80秒 100秒 120秒 理論上の下限 は、依存先の compile が終わるまで compile できない。依存をたどって一番長くつながる作業の列は、コアがいくら空いていても 約 70 秒 package 1 つずつ順にしか進まない。その列の作業時間を足すと 68〜71 秒 © LayerX Inc. 下限は並列 1 の 3 回の記録から計算(各作業が空いているときの時間で計算した見積もり。中央値 70 秒) 23
  21. 仮説 起動時のセットを 1 回の go build にまとめてみる まとめるのは go build

    の呼び出しだけ。ビルドするのは必要な分で、実行ファイルもサービスごとのまま 全サービスを 1 つのバイナリに go build × 1 サービスごとに go build(今) go build go build go build 必要な分を 1 回の go build で go build × 1 今必要な 12 個分 … × 12 全サービス(約 130 個)分 今必要な 12 個分 巨大なバイナリ × 1 + 重複はない - 使わないサービスまでビルドする - 1 行変えても、全部 link し直し + 必要な分だけ。1 個ずつ独立 - 共通の package を何度も compile(空の キャッシュで 125 秒) 共通の package © LayerX Inc. 重複した compile サービス固有の package + 必要な分だけで、重複がなくなるはず + 再起動は今どおり 1 個だけ 実行ファイル ※ -o で複数の実行ファイルを出すときは、名前が重ならないように(同じ名前だとエラーなしで上書きされる) 24
  22. 検証 検証: まとめると 125 秒 → 98 秒 申請・経費精算の 12

    個 · 空のキャッシュから 今: サービスごとに go build(2 個ずつ) まとめる: 12 個を 1 回の go build で 理論上の下限(見積もり) 125 秒 98 秒(×1.28) 約 70 秒 18 まとめたときに使っているコア数(18コア中) 0 0秒 20秒 40秒 60秒 最後まで残るのは、⼀番⻑い依存の流れの最後のcompile(27秒) + 重複した compile 394 → 0 回 + 2 回目以降の起動(キャッシュあり)でも速い 16 → 10 秒 + コードを直して再起動するときは、今までどおり そのサービスだけ ビルド © LayerX Inc. compile link 80秒 100秒 - メモリのピークが増える 約 11.5 → 15 GB - compile エラーは、全体のビルドが終わってから出る 各 3 回の中央値 · 下限は依存の一番長い流れ(空いているときの作業時間で見積もり)· メモリは go / compile / link の合計の最大 · 18 コア / 48GB の Mac 25
  23. まとめ まとめ 依存を共有する go build を同時に何個も走らせると、共通の package を それぞれがコンパイルする (重複)。ビルドキャッシュは、コンパイルが終わった結果しか共有しないため

    同時に走らせる数を 9 → 2 に減らしたら、起動時のビルドが 2.4 倍速くなった(290 → 122 秒) 2 が速いのは、go build 1 個が残すコアの隙間を 2 個目が埋めるから。3 個目からは、増えた CPU が ほぼ重複に回る さらに 1 回の go build にまとめる と重複は 0 になり、約 2 割速くなった。ただし、メモリのピークが 増える、compile エラーの結果が出るまでの待ちが長くなる、といったデメリットもある 最適な数は環境で変わる。設定を変える前に、まず 内部の様子を測ってみる と良い coding agent で PR の数が増える今、ビルドの速さは開発のボトルネックにも CI のコストにも 直結する。高速化はますます重要 になっている © LayerX Inc. 申請・経費精算を開くときの 12 個 · 18 コアの Mac · 空のキャッシュから · 3 回の中央値 26
  24. Appendix · ビルドの並列度 1. 外側の並列度:同時に動かす go build の数 1 外側の並列度

    同時に動かす go build の数(図は 2) go build(サービス 1) 2 -p go build 1 個の中で同時に走らせる作業の数 compile compile compile compile compile … link go build(サービス 2) compile compile compile compile compile … link 3 -c 点線の compile 1 つの中 関数 A 関数 B 関数 C 関数 D 関数ごとの機械語の生成を -c 個まで 並列にする 順番待ち サービス 3 サービス 4 … サービス 12 go build を呼ぶ側(lxpm やスクリプト)が決める、同時に動かす go build の数。Go の設定ではない 1 台のマシンで同じキャッシュを複数のプロセスが同時に使っても安全。ただし作業は重複しうる(キャッシュは壊れない)[7] -o にディレクトリを渡すと、1 回の go build で複数の実行ファイルをそこへ書き出せる [1] © LayerX Inc. 出典: go help build [1]、cmd/go のソース cache.go [7] 30
  25. Appendix · ビルドの並列度 2. -p :go build 1 個の中で同時に走らせる作業の数 1

    外側の並列度 同時に動かす go build の数(図は 2) go build(サービス 1) 3 -c 点線の compile 1 つの中 2 -p go build 1 個の中で同時に走らせる作業の数 compile compile compile compile compile … link go build(サービス 2) compile compile compile compile compile … link 関数 A 関数 B 関数 C 関数 D 関数ごとの機械語の生成を -c 個まで 並列にする 順番待ち サービス 3 サービス 4 … サービス 12 go build 1 個の中で同時に動かすプログラム(ビルドのコマンドやテストバイナリなど)の数 [1] デフォルトは GOMAXPROCS。GOMAXPROCS はふつう、使える CPU の数になる [1] 1 個の go build の中では、同じ package を 1 回しかコンパイルしない [17] 指定は go build -p 4 、または環境変数 GOFLAGS=-p=4 [1][11] © LayerX Inc. 今回は指定していない → 18 コアの Mac では 18 · 出典: go help build [1]、exec.go [8]、action.go [17]、go help environment [11] 31
  26. Appendix · ビルドの並列度 3. -c :package 1 つのコンパイルの中の並列数 1 外側の並列度

    同時に動かす go build の数(図は 2) go build(サービス 1) 2 -p go build 1 個の中で同時に走らせる作業の数 compile compile compile compile compile … link go build(サービス 2) compile compile compile compile compile … link 3 -c 点線の compile 1 つの中 関数 A 関数 B 関数 C 関数 D 関数ごとの機械語の生成を -c 個まで 並列にする 順番待ち サービス 3 サービス 4 … サービス 12 package 1 つのコンパイルの中で、関数ごとの機械語の生成を複数コアで同時に進める数。型チェックなどは 1 本で進む [13] go build が min(4, GOMAXPROCS)〜GOMAXPROCS を自動で渡す [9] コンパイラのオプションを変えると、ビルドキャッシュは別の結果として扱う [2] 並列度を絞るなら、GOMAXPROCS を CPU の数より下げる(cmd/go のソースのコメント)[9] © LayerX Inc. compile を直接呼んだときの -c のデフォルトは、ドキュメント [4] では 1、実装 [12] では GOMAXPROCS · 出典: go help build [1]、go help cache [2]、exec.go [8]・gc.go [9]・compile.go [13] 32
  27. 計測条件 Appendix 18 コアの Mac(Apple M5 Pro / 48GB)、go1.27.3 申請・経費精算を開くときにビルドされる

    12 個のサービス 毎回空のビルドキャッシュから。並列 1・2・3・4・9 を 3 回ずつ、順番を入れ替えて測定 プロダクト A・B も同じ条件で測定 リモート開発環境: 8vCPU に絞って測定(2 回の平均) © LayerX Inc. 33
  28. Appendix -debug-actiongraph :作業ごとの記録を JSON で書き出す 重複の数・ビルドの再生・CPU 時間は、すべてこの記録から数えた Go のソースに書かれていること で指定する。undocumented,

    unstable なフラグ [14] ビルドの action graph を JSON で書き出す。最初に時刻なしで書き、ビルドの最後に 時刻つきで書き直す [8] 作業 1 つごとに、種類(Mode)・package・action ID・開始と終了の時刻・実行した コマンドが入る [10] action ID は作業の入力のハッシュで、ビルドキャッシュと同じハッシュ [15] CmdUser / CmdSys は、実行したコマンドの user / system の CPU 時間(ナノ秒)[16] go build -debug-actiongraph=file 今回の使い方 全回、すべての go build に付けて実行 1 回の計測の中で、Mode が build で ActionID が同じ作業が 2 回以上あれば、2 回目以降を重複として数えた ビルドの再生は、TimeStart〜TimeDone を帯にし、CPU 時間(CmdUser + CmdSys) をその長さで割ってコア数にした © LayerX Inc. 左は申請・経費精算の並列 2 の回で、net/http を compile した記録(一部の項目を省略) 34
  29. 参考資料 Appendix Go 公式ドキュメント(pkg.go.dev) [1] go help build(-p、-gcflags) [2] go

    help cache [4] go tool compile(-c) [11] go help environment(GOFLAGS) Go のソース(go1.27.3 · commit 2aae100) pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies pkg.go.dev/cmd/go#hdr-Build_and_test_caching pkg.go.dev/cmd/compile pkg.go.dev/cmd/go#hdr-Environment_variables [7] cache.go(複数プロセスでの共有) github.com/golang/go/blob/2aae100/src/cmd/go/internal/cache/cache.go#L84-L89 [8] work/exec.go(worker・実行順・-debug-actiongraph の github.com/golang/go/blob/2aae100/src/cmd/go/internal/work/exec.go#L76-L261 書き出し) [9] work/gc.go(-c の配り方) github.com/golang/go/blob/2aae100/src/cmd/go/internal/work/gc.go#L182-L255 [10] work/action.go(-debug-actiongraph の JSON の項目) github.com/golang/go/blob/2aae100/src/cmd/go/internal/work/action.go#L174-L200 [12] compile/internal/base/flag.go(-c のデフォルト) github.com/golang/go/blob/2aae100/src/cmd/compile/internal/base/flag.go#L161 [13] compile/internal/gc/compile.go(関数ごとの並列コンパイル) github.com/golang/go/blob/2aae100/src/cmd/compile/internal/gc/compile.go#L135-L192 [14] cfg/cfg.go(-debug-actiongraph の位置づけ) github.com/golang/go/blob/2aae100/src/cmd/go/internal/cfg/cfg.go#L105 [15] work/buildid.go(action ID) github.com/golang/go/blob/2aae100/src/cmd/go/internal/work/buildid.go#L26-L33 [16] work/shell.go(コマンドの CPU 時間の記録) github.com/golang/go/blob/2aae100/src/cmd/go/internal/work/shell.go#L649-L659 [17] work/action.go(同じ package の作業は 1 つにまとめる) github.com/golang/go/blob/2aae100/src/cmd/go/internal/work/action.go#L437-L447 その他 lxpm(layerx.go #5) © LayerX Inc. speakerdeck.com/izumin5210/layerx-go-5 35