差分ディレクトリ同期 vs artifact 一括転送、どちらが速いのか?
GitHub Actions でビルドした成果物を別リポジトリに push する際、よく使われる設計として次の 2 つがある。
• 差分ディレクトリ同期方式
ビルド結果(例:_site/dist/*)とターゲットリポジトリを比較し、変更のあったディレクトリだけ rsync で同期する。
• artifact 一括方式
dist 全体を tar.gz にまとめ、artifact として upload → download し、まとめて展開・commit する。
今回は特に、
[dist 直下に複数ディレクトリがあり,各ディレクトリの中身は index.html のみ]
という強い前提条件のもとで、どちらが速いのかを考える。
なぜ意見が割れるのか
議論が割れる理由は単純で、どのコストが支配的だと考えるかが異なるから。
• 一方の見方
「index.html 1 ファイルしかないなら、差分を取る意味は薄い。artifact で一括処理した方が速い」
• もう一方の見方
「artifact には固定コストがあり、変更が少ないなら差分同期の方が無駄な仕事をしない」
これは感覚論ではなく、コスト構造の捉え方の違い。
差分ディレクトリ同期方式の実際のコスト
差分方式で発生する主な処理は、
• dist 配下ディレクトリの列挙
• 各ディレクトリに対する diff -qr
• 変更があったディレクトリのみ rsync
• git add / commit
ここで重要なのは、
• diff は確かに N 回起動されるが、比較対象は index.html 1 ファイル
• rsync は 変更があった場合のみ 起動される
• GitHub Actions の ubuntu-latest では、diff / rsync の fork+exec コストは数 ms 程度
つまりこの方式は、
• 変更が少ない場合、ほとんど「比較して何もしない」
• 処理量は 変更ディレクトリ数に比例して増える
という性質を持つ。
差分方式の強みは「転送量削減」ではなく、
無操作(rsync も git 変更もなし)を大量に作れることにある。
artifact 一括方式の見えにくい固定コスト
artifact 方式は一見シンプルで、
• tar でまとめる
• upload
• download
• 展開して git add .
という O(1) 個の操作に見える。
ただし GitHub Actions における artifact は、
• ローカルコピーではなく GitHub Storage との HTTP 転送
• データ量が小さくても一定のレイテンシがある
• build ジョブと push ジョブが分かれ、VM が切り替わる
という性質を持つ。
その結果、
• データが数 KB でも数秒〜十数秒の固定コストが発生
• ディレクトリ数が少なくても下限時間が縮まらない
artifact 方式は
「小さいと速い」方式ではなく、「大きくなってもあまり遅くならない」方式。
「git が最終的に差分を見るから同じ」ではない
よくある主張として、
どうせ最後に git が差分を見るのだから、前段で差分を取る意味はない
というものがあるが、これは正確ではない。
• 差分方式:git が見るのは 変更されたディレクトリのみ
• artifact 方式:git add . により 全ファイルを stat / hash
index.html しかなくても、ディレクトリ数が増えれば
git の stat コストは確実に積み上がる。
差分方式は「差分計算を git に押し付けていない」という点で意味がある。
計算量の整理(実用寄りのモデル)
現実的には次のように考えると分かりやすい。
• 差分方式
O(N · diff) + O(K · rsync)
• artifact 方式
O(artifact 固定コスト) + O(N · git stat)
ここで N はディレクトリ数、K は変更されたディレクトリ数。
artifact の固定コストは N に関係なく存在する。
N が数十〜数百、K が小さいケースでは、
artifact の固定コストが支配項になる可能性が高い。
結論:どちらを選ぶべきか
速度だけに注目した場合、
• 変更が少ない、今後構造が増える可能性がある
→ 差分ディレクトリ同期方式が合理的
• 常に全体更新、構造がほぼ固定、単純さ重視
→ artifact 一括方式が妥当
重要なのは、
最適化が効くかどうかは「データが小さいか」では決まらない
「無駄な仕事をどれだけ避けられるか」で決まる
という点。
この観点を持って選べば、どちらを採用しても後悔しにくい。