Git LFS vs. 標準Git: 大規模アセットにおけるパフォーマンス

大規模バイナリアセットの管理において、標準GitとGit Large File Storage(LFS)を使用した場合の重要なパフォーマンスの違いを理解しましょう。このガイドでは、Git LFSがリポジトリの肥大化を防ぎ、ポインタシステムを採用することでクローンおよびチェックアウト操作を劇的に高速化し、帯域幅の消費を削減する方法を説明します。マルチメディア、デザインアセット、大規模データセットなどのファイルに対して、いつ、どのようにLFSトラッキングを実装して、効率的で管理しやすいバージョン管理ワークフローを維持するかを学びましょう。

40 ビュー

Git LFS 対 標準Git:大規模アセットにおけるパフォーマンスへの影響

分散型バージョン管理システムの基盤であるGitは、テキストベースのソースコードの変更追跡において優れています。その効率性は、履歴全体にわたる小さな増分変更を管理するためにデルタ圧縮を利用するコンテンツアドレス指定ストレージに大きく依存しています。しかし、このモデルは、マルチメディアアセット、ゲームテクスチャ、大規模データセットなどの大きなバイナリファイルに適用されると、著しいパフォーマンスの課題に直面します。

非テキストデータに大きく依存するプロジェクトでは、標準Gitの使用はすぐにリポジトリの肥大化、クローン作成時間の遅延、リソースの非効率性につながります。本記事では、標準GitとGit Large File Storage (LFS) の包括的なパフォーマンス比較を提供し、それぞれのメカニズムを詳述するとともに、LFSが大規模アセットを効率的に管理するための必要な最適化ツールとなるのはいつかを特定します。


標準Gitのパフォーマンスボトルネック

Git LFSが存在する理由を理解するには、まず標準Gitがファイルをどのように処理するか、特にこのアプローチが大きなバイナリに対してなぜ機能しないのかを調べる必要があります。

コンテンツアドレス指定ストレージと履歴

Gitの核となる設計原則は、コミットされた全ファイルバージョンのすべてがリポジトリ履歴(.gitディレクトリ内)に保存されるべきであると規定しています。リポジトリがクローンされる際、すべての履歴データ—大きなバイナリファイルのすべてのバージョンを含め—がローカルマシンに転送されます。

このアプローチがバイナリファイルに対して効果が低い理由は、主に次の2点です。

  1. 非効率なデルタ圧縮: JPEG、MP4、またはコンパイルされた実行可能ファイルなどのバイナリファイルは、多くの場合すでに圧縮されています。これらのファイルに対して小さな変更のみが加えられた場合、Gitは意味のあるデルタを生成するのに苦労し、多くの場合、リビジョンごとにファイル全体のほぼ完全なコピーが履歴に保存されることになります。これにより、リポジトリサイズの増大が急速に加速します。
  2. 必須の履歴転送: リポジトリのクローン作成には、履歴全体をダウンロードする必要があります。プロジェクトに100MBのテクスチャファイルがあり、それが50回変更されている場合、最初のクローンでは、その単一のアセットの履歴だけで数ギガバイトを転送する必要があります。これは、特に新規貢献者やCI/CDシステムにとって、開発速度に深刻な影響を与えます。

結果: リポジトリは巨大になり、クローン時間が長くなり、バックグラウンドでのメンテナンスタスク(ガベージコレクションなど)が遅くなり、過剰なローカルディスクスペースが必要になります。

Git Large File Storage (LFS) の紹介

Git LFSはGitHubによって開発された(現在は広く採用されている)オープンソースの拡張機能で、Gitが特定のファイルタイプを処理する方法を変更します。LFSはストレージの負担をコアGitリポジトリから外し、ソースコードの効率性を維持しつつ、大きなバイナリを外部化します。

ポインターシステム

ファイルがLFSによって追跡される場合、実際のバイナリコンテンツはGitオブジェクトデータベースには保存されません。代わりに、LFSはGitリポジトリ内に標準化された小さなテキストポインターファイルを保存します。このポインターは、専用のLFSサーバー(通常はGitリモート(例:GitHub、GitLab、Bitbucket)と並行してホストされている)に保存されている実際のバイナリコンテンツの場所を参照します。

LFSポインターファイルは次のような外観をしています。

version https://git-lfs.github.com/spec/v1
oid sha256:4c2d44962ff3c43734e56598c199589d8995a643...a89c89
size 104857600

パフォーマンス上の利点:ジャストインタイム取得

LFSの基本的なパフォーマンス上の利点は、クローンやフェッチなどの操作中に、Gitが小さなテキストポインターのみを取得することです。実際の大きなバイナリファイルは、明示的に必要とされたとき、通常はチェックアウト操作(git checkoutまたはgit lfs pull)の際にのみダウンロードされます。

パフォーマンス比較:LFS 対 標準Git

次の表は、大規模アセットを管理する際の重要な開発操作全体でのパフォーマンスの違いをまとめたものです。

操作 標準Gitのパフォーマンス Git LFSのパフォーマンス メリット 根拠
初期クローン 低い/非常に遅い 非常に良い/速い LFS 小さなポインターのみがダウンロードされ、バイナリはオンデマンドで取得されます。
リポジトリサイズ 非常に大きい(肥大化) 小さい(スリム) LFS バイナリは.gitディレクトリから外部化されます。
チェックアウト/切り替え 遅い/高いI/O 速い LFS 必要な特定のバイナリバージョンをHTTP経由で取得します。
CI/CDビルド時間 遅い(巨大なクローンが原因) 速い LFS クローンと依存関係のフェッチに費やされる時間が大幅に短縮されます。
履歴レビュー 完全な履歴のダウンロードが必要 ポインターのみ(速い) LFS 履歴はスリムで管理しやすくなります。

1. リポジトリの肥大化とメンテナンス

標準Gitリポジトリは、大きなアセットがコミットされた後、それらのアセットが後で削除されたとしても(履歴には残る)、クリーンアップが非常に困難であることが知られています。これには、履歴を永続的に書き換えるためのgit filter-branchgit filter-repoのような複雑なツールが必要となり、破壊的で時間のかかるプロセスとなります。

LFSの影響: LFSは大きなファイルを外部化するため、コアGitリポジトリのサイズは一貫して小さく管理しやすくなり、ガベージコレクション(git gc)のような内部Gitプロセスに必要な時間が劇的に短縮されます。

2. 帯域幅とネットワーク遅延

分散型チームにとって、ネットワーク帯域幅は主要な懸念事項です。

  • 標準Git: すべてのユーザーはリポジトリ履歴全体をプルする必要があり、実際に必要とするファイルに関係なく、新しいクローンごとに大量の帯域幅を消費します。
  • Git LFS: LFSは現在チェックアウトされているコミットに関連する特定のバイナリブロブのみを転送します。ユーザーが最新のリリースブランチのみで作業する場合、その特定のバージョンに必要なバイナリのみをダウンロードするため、特に低速な接続で、帯域幅を大幅に節約し、プロセスを高速化します。

3. サーバー負荷

巨大なリポジトリを管理することは、特に大量のデータをフェッチまたはプッシュするような深い操作中に、Gitサーバーに高い負荷をかけます。大規模なファイルストレージのメカニズムを別個の最適化されたLFSサーバー(多くの場合、単純なHTTPやS3のようなオブジェクトストレージプロトコルを使用)に移行することにより、コアGitサーバーは標準的なソースコード操作に対してパフォーマンスを維持できます。

Git LFSを使用するタイミング

Git LFSは、次のすべての基準を満たすファイルに対して最適な選択肢です。

  1. サイズが大きい: 一般的に、500KBから1MBを超えるファイル。
  2. バイナリ形式: 圧縮があまり得意でないファイル(例:圧縮された画像、ビデオ、オーディオ)。
  3. 頻繁な変更: 頻繁に更新され、履歴内に繰り返しのバージョンを生成するファイル(例:開発中のゲームアセット)。

LFS追跡の一般的な候補:

  • *.psd*.tiff*.blend*.max(デザイン/3Dアセット)
  • *.mp4*.mov*.wav(メディアファイル)
  • *.dll*.exe*.jar(コンパイル済みバイナリ、コミットされる場合)
  • 大規模な*.csv*.parquet、またはデータベーススナップショット(データサイエンス)

Git LFSの実装

LFSの実装は簡単で、LFSクライアントをインストールし、追跡すべきファイルパターンを指定する必要があります。

ステップ 1: LFSのインストールと初期化

まず、お使いのマシンにGit LFSクライアントがインストールされていることを確認します。次に、リポジトリ内で一度インストールコマンドを実行します。

git lfs install

ステップ 2: ファイルタイプの追跡

git lfs trackを使用して、GitにどのファイルパターンをLFSで管理するかを伝えます。このコマンドは、LFSが正しく機能するために不可欠な.gitattributesファイルを作成または更新します。

例:すべてのPhotoshopファイルと大きなビデオファイルの追跡

git lfs track "*.psd"
git lfs track "assets/*.mp4"

# .gitattributesに加えられた変更を確認
cat .gitattributes
# 出力例:
# *.psd filter=lfs diff=lfs merge=lfs -text
# assets/*.mp4 filter=lfs diff=lfs merge=lfs -text

ステップ 3: コミットとプッシュ

重要な点として、追跡対象のファイルと一緒に.gitattributesファイルをコミットする必要があります。プッシュすると、Gitはポインターを転送し、LFSクライアントは大きなバイナリをLFSストアにアップロードする処理を行います。

git add .gitattributes assets/
git commit -m "LFSで追跡されるPSDとMP4を追加"
git push

⚠️ ベストプラクティス:.gitattributesを最初にコミットする

.gitattributesファイルは、追跡対象の大きなファイルより前または同時にコミットされる必要があります。大きなファイルを先にコミットすると、Gitはそれをネイティブに追跡し、LFSの目的が無駄になります。

結論

標準Gitは、その意図された目的、すなわちソースコードと小さな設定ファイルのバージョン管理において比類がありません。しかし、大きなバイナリアセットが導入されると、リポジトリの肥大化と履歴の必須転送により、そのパフォーマンスは急速に低下します。

Git LFSは、大規模ファイルのストレージを抽象化することで決定的なパフォーマンス最適化を提供し、コアGitリポジトリが軽量で、クローンが速く、管理しやすい状態であることを保証します。ポインターシステムとジャストインタイムフェッチングを利用することにより、LFSは以前は遅かった操作を迅速なプロセスに変え、ゲーム開発、データサイエンス、および頻繁に更新される実体のあるバイナリアセットを扱うあらゆるプロジェクトにとって不可欠なツールとなります。