概要

概要

概要

質問は次のように構成されています。
まず、私がこのトピックに興味を持っている理由と、このトピックが私が進行中の問題をどのように解決するかについての背景知識を提供します。その後、ファイルシステムのキャッシュについて実際に別の質問をするので、同期(一部のC ++プロジェクトビルド設定)に興味がない場合は、最初の部分をスキップしてください。

元の質問:共有ライブラリのリンク

プロジェクトのビルド時間を短縮する方法を探しています。設定は次のとおりです。ディレクトリ(と呼ばれるworkarea)はNFS共有にあります。最初はソースコードとmakefileのみが含まれています。その後、ビルドプロセスは最初に静的ライブラリを作成し、次にworkarea/lib静的workarea/dllライブラリを使用して共有ライブラリを作成しますworkarea/lib。共有ライブラリの作成中に、これらのライブラリは書き込まれるだけでなく、リンク時にシンボルがnm失われていないことを確認するために再読み込みされます。多くのタスクを並列に使用すると(たとえば、make -j 20またはmake -j 40)、ビルド時間がリンク時間によって迅速に影響を受ける可能性があります。この場合、リンクのパフォーマンスはファイルシステムのパフォーマンスによって制限されます。たとえば、20 個のタスクを並列に接続するには、NFS 共有では約 35 秒かかりますが、RAM ドライブでは 5 秒しかかかりません。 rsyncを使用してNFS共有にコピーするのにdll6秒かかるため、RAMドライブで作業してからNFSに同期する方がNFS共有で直接作業するよりもはるかに高速です。 NFS共有とRAMドライブ間でファイルを明示的にコピー/リンクせずに高速なパフォーマンスを実現する方法を探しています。
NFS共有はすでにキャッシュを使用していますが、このキャッシュは読み取りアクセスのみをキャッシュできます。
AFAIK、NFSでは、NFSサーバーが書き込みが完了したことを確認するまで、すべてのNFSクライアントが書き込みを承認できないため、クライアントはローカル書き込みバッファを使用できず、書き込みスループット(最大ピークを含む)はネットワーク速度によって制限されます。私たちの設定では、結合された書き込みスループットを約80 MB / sに効果的に制限します。
ただし、読み取りキャッシュを使用すると、読み取りパフォーマンスがはるかに向上します。生成されたコンテンツをNFSに接続しdll、RAMドライブにシンボリックリンクで接続すると、パフォーマンスは依然として良好です(約5秒)。ビルドプロセスを完了するには、NFS共有に常駐する必要があります。高速増分ビルドを可能にするには、共有(または永続マウント)に存在し、ビルドを開始するシステムからアクセスできるようにNFSに存在する必要があります。このDLLを使用して作業します。したがって、以下の問題に対する解決策を適用したいと思います。この方法も効果があるかもしれません(後者はコンパイル時間を短縮することです)。以下のクイックセットアップ時間の要件は、必要なときにのみデータをコピーしてクイック増分ビルドを実行する必要があるためです。workarea/libworkarea/dllworkarea/*libdll
workarea/dllworkarea/lib

修正する

ビルド設定についてより具体的に説明する必要があります。詳細は次のとおりです。コンパイル単位は、一時ディレクトリ(/ tmp)にある.oファイルにコンパイルされます。それlibからar。全体のビルドプロセスは段階的です。

  • コンパイル単位は、コンパイル単位自体(.Cファイル)または埋め込みヘッダーが変更された場合(含まれているコンパイラを使用して生成された依存ファイル)のみがmake再コンパイルされます。
  • 静的ライブラリは、そのコンパイル単位の1つが再コンパイルされたときにのみ更新されます。
  • 共有ライブラリは、静的ライブラリのいずれかが変更された場合にのみ再リンクされます。共有ライブラリのシンボルは、共有ライブラリが依存する共有ライブラリによって提供されるシンボルが変更された場合、または共有ライブラリ自体が更新された場合にのみ再確認されます。

gccそれにもかかわらず、複数のコンパイラ(、、clang)、コンパイラバージョン、コンパイルモード(release、、debug)、C ++標準(C++97C++11)、および追加の修正(例)を使用できるため、完全またはほぼ完全な再構築が必要なことがよくあります。libubsanすべての組み合わせは異なるディレクトリを効果的に使用するため、設定を切り替えてそのlib設定dllの最後のビルドに基づいて徐々にビルドすることができます。また、増分ビルドを使用すると、通常は少数のファイルを再コンパイルするだけで時間がかかりませんが、共有ライブラリの(潜在的に大きな)再リンクをトリガすることで、はるかに時間がかかります。

アップデート2

その間、私はNFSマウントオプションについて学びました。このオプションは、Linuxを除くnoctoすべてのNFS実装で常に書き込みバッファをフラッシュする問題を解決します。私たちはいくつかの他のことを試しました。ローカルNFSサーバーを書き込みバッファとして有効にし、デフォルトのNFSマウントをエクスポートします。ただし、この場合、残念ながら、NFSサーバー自体には書き込みバッファーはありません。これは、サーバーがデフォルトのファイルシステムを安定したストレージにフラッシュせず、デフォルトのファイルシステムが書き込みバッファを使用する場合に暗黙的に書き込みバッファを使用することを意味するようです(ファイルシステムの場合のようです)。ドライブの実際の位置から)。基本的なNFS共有をマウントし、書き込みバッファを提供し、他のNFSサーバーを介してこのバッファ付きマウントを提供するのと同じシステムでLinux以外のVMを使用するオプションも検討しましたが、まだテストしていないことを避けてください。そのような解決策。また、キャッシュベースの複数のファイルシステムラッパーがキャッシュとして使用されていることがわかりましたが、どちらも書き込みバッファリングを実装していません。close()noctoasyncasync
nocto
FUSE

キャッシュおよびバッファディレクトリ

origNFS共有など、遅いファイルシステムにあるディレクトリ(ディレクトリと呼びます)を考えてみましょう。非常に短い時間(数秒または数分ほど重要ではありません)、ローカルハードドライブやメモリドライブなどの高速ファイルシステム上のディレクトリをorig使用して、完全にキャッシュされバッファされたビューを作成したいと思います。cache。キャッシュはマウントなどを介してアクセスできる必要があり、cached_viewroot権限は必要ありません。私はキャッシュの寿命の間に直接読み取りまたは書き込みアクセスがないと仮定しますorig(もちろん、キャッシュ自体は除く)。完全にキャッシュされバッファリングされるということは、次のことを意味します。

  1. クエリをファイルシステムに渡し、その結果をキャッシュし、その時点から使用してorig読み取りクエリに応答します。
  2. 書き込みクエリは直接作成され、cache完了時に承認されます。つまり、キャッシュも書き込みバッファです。close()これは、ファイルへの書き込み時に呼び出される場合にも発生する必要があります。その後、バックグラウンドで書き込みがに渡されます(おそらくキューを使用して)origcacheもちろん、作成されたデータに対する読み取りクエリは、進行中のデータを使用して応答されます。

また、以下が必要です。

  1. キャッシュは、すべてのペアをフラッシュするキャッシュをオフにする機能を提供しますorig。フラッシュの実行時間は、すべてのファイルではなく、書き込まれるファイルのサイズによって異なります。その後、人々は安全にorig再び訪問することができます。
  2. 設定時間が早いです。たとえば、キャッシュの初期化はファイルサイズではorigなくファイル数にのみ依存する可能性があるため、一度origコピーすることはorigオプションではありません。cache

最後に、他のファイルシステムをキャッシュとして使用するのではなく、メインメモリ(サーバーに十分なRAMがある)にのみキャッシュするソリューションも必要です。私が知っている限り、NFSはバッファへの書き込みを許可しないため、NFSなどの組み込みキャッシュを使用することはオプションではありません(パート1を参照)。

orig私の設定では、toの内容をシンボリックリンクし、次を使用cacheして少し悪い動作をシミュレートできますcache(すべての書き込みは実際にファイルを新しいファイルに置き換えるため、シンボリックリンクはバージョンの置き換えで更新されます)。その後、変更されたファイルをorig後で。これは上記の要件を完全に満たしていません。たとえば、読み取りは一度だけ実行されず、ファイルはシンボリックリンクに置き換えられ、これはもちろん一部のアプリケーションに影響します。
私はこれがこの問題を解決する正しい方法だとは思わない。 (簡単な設定でも)誰かがよりクリーンで迅速な解決策を知っているかもしれません。

答え1

と、まだ「overlayfs」と答えた人が誰もいないというのが驚きですね。

実際、私は2つの提案をしたいと思います。 1つ目はoverlayfsを使用することですが、基本的に説明するものと同じですが、注意することがあります。 Overlayfs(Linux 3.18以降の標準)を使用すると、2つの仮想的にマージされたディレクトリツリーからデータを読み取り、そのうちの1つのみを書き込むことができます。あなたがする必要があるのは、クイックストア(tmpfsなど)を選択してNFSボリュームに上書きし、両方のオーバーレイマージでコンパイルを実行することです。完了すると、NFS 内のすべてのファイルに書き込みはなく、他のファイルシステムはすべての変更を保持します。変更を保存するには、NFSに再同期するだけです。興味のないファイルを除外したり、結果からいくつかのファイルを手動で選択したりすることもできます。

私の小さなプロジェクトで比較的単純なoverlayfsの例を見ることができます。https://github.com/nrdvana/squash-portage/blob/master/squash-portage.sh また、このスクリプトは、オーバーレイfsなしで古いカーネルを使用している場合にUnionFSを使用する方法を示しています。

私の場合、Gentooは何百万もの小さなディスク書き込みを実行するため、rsyncコマンドを使用してソフトウェアストアを更新するのに非常に長い時間がかかります。私はoverlayfsを使用してtmpfsに対するすべての変更を記録し、mksquashfsを使用してツリーの圧縮イメージを作成します。それからtmpfsをダンプし、その場所に圧縮された画像をマウントしました。

2番目の提案は「木から」構築することです。アイデアは、ソースコードとmakefileを1つのツリーに配置し、最初のツリーをミラーリングする別のツリーにすべての中間ファイルを生成するようにautomakeに指示することです。

運が良ければ、ビルドツール(automakeなど)はすでにこれを行うことができます。運が悪い場合は、makefileに触れて時間を費やす必要があるかもしれません。

関連情報