依存性の分離とコンテナ化によるコンパイルの2.5倍高速化
コンパイル時間は、開発プロセスを通じて変化する複雑な内部依存関係や外部依存関係、さらにはオペレーティング・システムやハードウェア・アーキテクチャなどのコンパイル環境の変化によって、さらに複雑になる可能性がある。以下は、大規模なAIまたはMLOpsプロジェクトに取り組む際に遭遇する可能性のある一般的な問題です:
阻害的に長いコンパイル - コード統合は毎日何百回も行われる。何十万行ものコードが存在するため、わずかな変更であっても、フル・コンパイルに通常1時間以上かかる可能性がある。
複雑なコンパイル環境 - プロジェクトのコードは、CentOSやUbuntuなどの異なるオペレーティング・システム、GCC、LLVM、CUDAなどの基本的な依存関係、ハードウェア・アーキテクチャを含む、異なる環境下でコンパイルする必要があります。通常、特定の環境でのコンパイルは、異なる環境では動作しないかもしれません。
複雑な依存関係 - プロジェクトのコンパイルには、30以上のコンポーネント間依存関係やサードパーティ依存関係が含まれます。プロジェクトの開発は、しばしば依存関係の変更につながり、必然的に依存関係のコンフリクトを引き起こします。依存関係間のバージョン管理は非常に複雑であるため、依存関係のバージョンを更新すると、他のコンポーネントに影響を与えやすくなります。
サードパーティ依存ライブラリのダウンロードに時間がかかるか、失敗する - ネットワークの遅延や不安定なサードパーティ依存ライブラリにより、リソースのダウンロードに時間がかかったり、アクセスに失敗したりして、コードの統合に深刻な影響を与える。
依存関係を切り離し、テストのコンテナ化を実装することで、オープンソースの埋め込み類似検索プロジェクトMilvusの作業中に、平均コンパイル時間を60%短縮することに成功しました。
プロジェクトの依存関係を切り離す
プロジェクトのコンパイルには、通常、多数の内部および外部コンポーネントの依存関係が伴います。プロジェクトの依存関係が多ければ多いほど、その管理は複雑になります。ソフトウェアが大きくなるにつれて、依存関係を変更したり削除したりするのは難しくなり、コストもかかるようになります。開発プロセス全体を通じて、依存関係が適切に機能するように定期的なメンテナンスが必要である。 不十分なメンテナンス、複雑な依存関係、あるいは欠陥のある依存関係は、開発を遅らせたり停滞させたりするコンフリクトを引き起こす可能性がある。実際には、リソースのダウンロードの遅れや、コード統合に悪影響を与えるアクセス障害などを意味します。プロジェクトの依存関係をデカップリングすることで、不具合を軽減し、コンパイル時間を短縮することができます。
したがって、プロジェクトの依存関係を切り離すことをお勧めします:
- 複雑な依存関係を持つコンポーネントを分割する
- バージョン管理には異なるリポジトリを使用する。
- 設定ファイルを使用して、バージョン情報、コンパイル・オプション、依存関係などを管理する。
- 設定ファイルをコンポーネント・ライブラリに追加し、プロジェクトが反復するたびに更新されるようにする。
コンポーネント間のコンパイル最適化 - 設定ファイルに記録された依存関係やコンパイルオプションに従って、関連するコンポーネントをプルしてコンパイルします。バイナリ コンパイル結果と対応するマニフェスト ファイルにタグ付けして梱包し、プライベート リポジトリにアップロードします。コンポーネントまたはコンポーネントが依存するコンポーネントに変更がない場合は、マニフェスト ファイルに従ってコンパイル結果を再生します。ネットワークの遅延や不安定なサードパーティ依存ライブラリなどの問題については、内部リポジトリをセットアップするか、ミラーされたリポジトリを使用してみてください。
コンポーネント間のコンパイルを最適化するには
1.依存関係グラフの作成 - コンポーネント・ライブラリの構成ファイルを使用して、依存関係グラフを作成します。依存関係を使用して、上流と下流の依存コンポーネントのバージョン情報(Git Branch、Tag、Git commit ID)やコンパイルオプションなどを取得します。
図1](https://assets.zilliz.com/1_949dffec32.png)
2.依存関係のチェック - 循環依存関係、バージョンの競合、コンポーネント間で発生するその他の問題に対するアラートを生成します。
3.Flatten dependencies - Depth First Search (DFS)によって依存関係をソートし、重複する依存関係を持つコンポーネントをフロントマージして依存関係グラフを形成します。
図2](https://assets.zilliz.com/2_45130c55e4.png)
4.MerkleTreeアルゴリズムを使用して、バージョン情報、コンパイルオプションなどに基づいて、各コンポーネントの依存関係を含むハッシュ(ルートハッシュ)を生成します。コンポーネント名などの情報と組み合わせることで、このアルゴリズムは各コンポーネントに固有のタグを形成する。
図3](https://assets.zilliz.com/3_6a4fcdf4e3.png)
5.コンポーネントの一意なタグ情報に基づいて、対応するコンパイルアーカイブがプライベートリポジトリに存在するかどうかをチェックする。コンパイルアーカイブが検索された場合は、それを解凍して再生用のマニフェストファイルを取得します。そうでない場合は、コンポーネントをコンパイルし、生成されたコンパイルオブジェクトファイルとマニフェストファイルをマークアップして、プライベートリポジトリにアップロードします。
このようにします。
**コンパイルされたオブジェクトファイルをキャッシュするために、言語固有のコンパイルキャッシュツールを選択し、プライベートリポジトリにアップロードして保存します。C/C++コンパイルでは、CCacheのようなコンパイル・キャッシュ・ツールを選択してC/C++コンパイルの中間ファイルをキャッシュし、コンパイル後にローカルのCCacheキャッシュをアーカイブします。このようなコンパイル・キャッシュ・ツールは、コンパイル後に変更されたコード・ファイルを1つずつキャッシュし、変更されていないコード・ファイルのコンパイル済みコンポーネントをコピーして、最終的なコンパイルに直接関与できるようにするだけである。 コンポーネント内のコンパイルの最適化には、以下のステップが含まれる:
1.必要なコンパイル依存関係をDockerfileに追加する。Hadolintを使ってDockerfileのコンプライアンスチェックを行い、イメージがDockerのベストプラクティスに準拠していることを確認する。 2.プロジェクトのスプリントバージョン(バージョン+ビルド)、オペレーティングシステム、その他の情報に従って、コンパイル環境をミラーリングする。 3.ミラーリングしたコンパイル環境コンテナを実行し、イメージIDを環境変数としてコンテナに転送する。以下は、イメージIDを取得するコマンドの例です。「docker inspect ' - type=image' - format '{{.ID}}' repository/build-env:v0.1-centos7」。 4.適切なコンパイルキャッシュツールを選択します:コードを統合してコンパイルするコンテナを入力し、適切なコンパイルキャッシュが存在するかどうかをプライベートリポジトリで確認します。存在する場合は、ダウンロードして指定したディレクトリに解凍します。すべてのコンポーネントがコンパイルされた後、コンパイルキャッシュツールによって生成されたキャッシュがパッケージ化され、プロジェクトのバージョンとイメージIDに基づいてプライベートリポジトリにアップロードされます。
さらなるコンパイルの最適化
私たちの初期ビルドでは、ディスク容量とネットワーク帯域幅の占有が大きく、デプロイに時間がかかるため、次のような対策を講じた: 1.1. アルパイン、ビジーボックスなど、最も無駄のないベースイメージを選択し、イメー ジサイズを縮小する。 2.イメージレイヤーの数を減らす。依存関係をできるだけ再利用する。複数のコマンドを"&&"でマージする。 3.画像構築中の中間生成物をクリーンアップする。 4.できるだけイメージキャッシュを使用してイメージを構築する。
プロジェクトが進むにつれて、コンパイル・キャッシュが増加する一方で、一部のコンパイル・キャッシュが十分に利用されていないため、ディスク使用量とネットワーク・リソースが急増し始めました。そこで、次のような調整を行った:
定期的にキャッシュファイルをクリーンアップする - プライベートリポジトリを定期的にチェックし(たとえばスクリプトを使用)、しばらく変更がなかったり、あまりダウンロードされていないキャッシュファイルをクリーンアップします。
選択的なコンパイル・キャッシュ - リソースを必要とするコンパイルだけをキャッシュし、あまりリソースを必要としないコンパイルのキャッシュはスキップします。
選択的コンパイルキャッシュ - リソースを必要とするコンパイルのみをキャッシュし、リソースをあまり必要としないコンパイルはキャッシュしません。
エラーを減らし、安定性と信頼性を向上させるためのコンテナ化テストの活用
コードはさまざまな環境でコンパイルする必要があり、それにはさまざまなオペレーティング・システム(CentOSやUbuntuなど)、基本的な依存関係(GCC、LLVM、CUDAなど)、特定のハードウェア・アーキテクチャが含まれます。特定の環境で正常にコンパイルされたコードは、異なる環境では失敗する。コンテナ内でテストを実行することで、テストプロセスはより速く、より正確になる。
コンテナ化によって、テスト環境の一貫性が確保され、アプリケーションが期待通りに動作することが保証される。コンテナ化されたテスト・アプローチは、テストをイメージ・コンテナとしてパッケージ化し、真に分離されたテスト環境を構築する。私たちのテスト担当者は、このアプローチがかなり有用であることに気づき、最終的にコンパイル時間を60%も短縮することができました。
**コンパイルされた製品はシステム環境の変化に敏感であるため、異なるオペレーティング・システムでは未知のエラーが発生する可能性があります。コンパイル環境の変化に応じて、コンパイル済み製品のキャッシュにタグを付けてアーカイブする必要がありますが、分類が困難です。そこで、このような問題を解決するために、コンパイル環境を統一するコンテナ化技術を導入した。
<br
結論
この記事では、プロジェクトの依存関係を分析することにより、コンポーネント間およびコンポーネント内でのコンパイル最適化のためのさまざまな手法を紹介し、安定的かつ効率的な継続的コード統合を構築するためのアイデアとベストプラクティスを提供した。これらの方法は、複雑な依存関係に起因するコード統合の遅延を解決し、コンテナー内の操作を統一して環境の一貫性を確保し、コンパイル結果の再生やコンパイルキャッシュツールを使用して中間コンパイル結果をキャッシュすることにより、コンパイル効率を向上させるのに役立ちました。
以上の実践により、プロジェクトのコンパイル時間は平均60%短縮され、コード統合の全体的な効率が大幅に改善された。今後は、コンポーネント間およびコンポーネント内でのコンパイルの並列化を継続し、コンパイル時間をさらに短縮していく。
*この記事で使用したソースは以下の通りです。
- ソースツリーをビルドレベルのコンポーネントにデカップリングする"
- "Factors to consider when adding third party dependencies to a project"
- "Surviving Software Dependencies"
- "依存関係を理解する:ソフトウェア開発における調整の課題に関する考察 "
著者について
Zhifeng ZhangはZilliz.comのシニアDevOpsエンジニアで、オープンソースのベクターデータベースであるMilvusに携わっている。広州ソフトウェア工学研究所でモノのインターネット(IoT)の学士号を取得。CI/CD、DevOps、ITインフラ管理、Cloud-Nativeツールキット、コンテナ化、コンパイルプロセスの最適化などの分野のプロジェクトに参加し、リーダーとしてキャリアを積んでいる。
読み続けて

Vector Lakebase: End the AI Data Silo
Learn how Vector Lakebase unifies vector search, data lakes, and AI data operations so teams can serve RAG and agents without copy-and-sync pipelines.

Why AI Databases Don't Need SQL
Whether you like it or not, here's the truth: SQL is destined for decline in the era of AI.

The Great AI Agent Protocol Race: Function Calling vs. MCP vs. A2A
Compare Function Calling, MCP, and A2A protocols for AI agents. Learn which standard best fits your development needs and future-proof your applications.


