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