Debianパッケージマネージャは、dpkg
sync_file_range()+ fsync()の代わりにAIO fsync()操作の1つを使用して、大幅なパフォーマンス向上を実現できますか?
[提案された] fsync2() API は、アプリケーションが避けたい同期方式である点を除けば、既存の AIO_FSYNC/AIO_FDSYNC API と本質的に同じです。
AIO_FSYNC [使用]に対する私の唯一の主張は、「実装は単に作業キューにすぎない」ということです。これはファイルシステムの実装とは無関係であるため、ほとんど意味がありませんが、実行されたすべてのfsync操作の自動カーネル側の並列化を可能にします。これにより、ファイルシステムは、同時fsync操作を完了するときに不要なログ書き込みを自動的に最適化できます。 XFS、ext4などは、ユーザーアプリケーションが多数のプロセス/スレッドで同時にfsync()を実行しているときにすでにこれを行っています。
この単純な実装により、XFSで単純な「aio fsyncに解凍」ワークロードを許可します(例:「4kBファイルの多くとaio_fsync()バッチ書き込み、新しいバッチをディスパッチする前に完了したfsync()を終了する」) 2000ファイル/秒(同期書き込みIOレイテンシ)範囲)最大40,000ファイル/秒以上(バックエンドストレージへのIOPS書き込み)
例のワークロードは、apt-get install
またはに似ていますdpkg -i
(部分的には、インストールされているパッケージのファイルサイズによって異なります:-)。 dpkg
解凍したすべてのファイルは、名前を変更する前に効果的にfsync()する必要があります。
dpkg
Ted T'soの提案に基づいて最適化されました。最適化は、特定の時点でsync_file_range()への呼び出しを追加することです。このシステムコールが行われました。いいえfsync() と同じ保証を提供します。ドキュメントを読む同期ファイル範囲()そして目立つ警告に注意してください:-).
これらの操作のどれもファイルのメタデータを記録しません。したがって、アプリケーションがインスタンス化されたディスクブロックの上書きを厳密に適用しない限り、競合後にデータを使用できるという保証はありません。
dpkg
SYNC_FILE_RANGE_WRITE
まず、パッケージ内のすべてのファイルを書き込むを使用して、各ファイルが作成されたらすぐにデータの書き込みを保存します。次に、ファイルを2回目に移動し、使用されたSYNC_FILE_RANGE_WAIT_BEFORE
データが書き換えられるのを待ってから、fsync()
最後にファイル名をその場所に変更します。
提出物を見る:
- sync(2) はデフォルトで無効になっています。
- 解凍時に安全なI / O操作を無効にするには、新しい--force-unsafe-ioを追加します。
- Linuxでは、できるだけ早く解凍されたファイルの書き込み保存を開始します。
- Linuxでは、fsyncの前に書き込み保存が行われます。
私の仮説は、fsync()タスクを並列化すると、より効率的なバッチ処理を可能にしてパフォーマンスを向上させることができるということです。メタデータディスクのメタデータが常に一貫しているようにするには、書き込み、特にバッチ関連バリア/ディスクキャッシュフラッシュが必要です。
編集:少なくともext4ファイルシステムを使用するとき、私の前提は単純すぎるようです。
2番目の一連のsync_file_range()呼び出しと操作は、
SYNC_FILE_RANGE_WAIT_BEFORE
以前に開始された書き込みの保存が完了するまでブロックされます。これにより、デフォルトで遅延割り当てが解決されたことが保証されます。つまり、データブロックが割り当てられて書き込まれ、inodeが(メモリ内で)更新されましたが、必ずしもディスクにプッシュされるわけではありません。[fsync()] 呼び出しは実際に inode をディスクに強制的に書き込みます。 ext4ファイルシステムの場合、最初の[fsync()]は実際にすべてのinodeをディスクにプッシュします。、以降のすべての[fsync()]呼び出しは事実上no-opsです(ファイル「a」、「b」、および「c」がすべて同じファイルシステムにあるとします)。しかし、これは(重い)jbd2コミット数を最小限に抑えることを意味します。
これはLinux固有のシステムコール ---sync_file_range() --- を使用しますが、その結果、すべてのファイルシステムに対して全体的に高速なパフォーマンスが得られます。したがって、私はこれがext4のハッキングだとは思わない。しかし、これはおそらくext4を他のファイルシステムよりも速くすることができるでしょう。
-テッドソ
他のファイルシステムでは、AIO fsync()操作を使用すると利点があります。
bcachefs
(開発中)ext4よりも異なるファイル間のIOをよりよく分離すると主張します。したがって、テストは特に興味深いかもしれません。
ext4は純粋なAIO fsync()モードに最適化されていないようです(他のファイルシステムにも同じ制約があると思います)。もしそうなら、同じsync_file_range()呼び出しをすべて最初に実行してから、すべてのAIO fsync()操作を2回目のパスで開始し、作業が完了したら、すべてのファイルの名前をfsync()に変更して完了することができると思いました。
古い:
そのような調査の最初のステップは測定でなければなりません :-)。
fsync()セクションはを使用して無効にできますecho "force-unsafe-io" > /etc/dpkg/dpkg.cfg.d/force-unsafe-io
。
これまで私はDebian 9コンテナで実行してみましたapt-get install
。strace -f -wc
たとえば、「unsafe io」aptitude
パッケージには495の同期fsync()呼び出ししかありません。通常のインストール中にはaptitude
1011のfsync()呼び出しがあります。 "unsafe io"はまた、このSYNC_FILE_RANGE_WAIT_BEFORE
呼び出しを無効にして、sync_file_range()呼び出しの数を1036から518に減らします。
しかし、これが平均時間を短縮するかどうかは不明です。この場合、実行間のランダム変動に過ぎないようです。これまで、機械式HDDのext4とXFSをテストしました。
apt-get
合計サイズが 21.7 MB の 518 個の解凍されたファイルを示します (下記の出力を参照)。
495 fsync() 呼び出しに関して "unsafe io" が要求された場合でも持続します。
ext4では、strace出力は残りのfsync()呼び出しに約11秒かかることを示しています。 XFSでは、対応する数値は約7秒です。すべての場合において、インストールにはほとんど時間がかかりますaptitude
。
したがって、「安全でないio」がインストールにわずかな改善をもたらしますが、違いが実際に目立つ前にシステムの残りの部分よりも速い(低レイテンシ)デバイスにインストールする必要aptitude
があるようです。/var
しかし、私はこのニッチのケースを最適化することに興味がありません。
実行を実行すると、strace -f -y -e trace=fsync,rename
残りのfsync()呼び出しの2つはオンになり、そのうち/etc/ld.so.cache~
493は/var/lib/dpkg/
パッケージデータベース内のファイルへの呼び出しであることを示しています。
fsync() 呼び出しは 318 にあります。/var/lib/dpkg/updates/
これはdpkgデータベースの増分です/var/lib/dpkg/status
。 dpkgの実行が完了すると、デルタはマスターデータベース(「チェックポイント」)にロールアップされます。
The following NEW packages will be installed:
aptitude aptitude-common libboost-filesystem1.62.0 libboost-iostreams1.62.0 libboost-system1.62.0 libcgi-fast-perl libcgi-pm-perl
libclass-accessor-perl libcwidget3v5 libencode-locale-perl libfcgi-perl libhtml-parser-perl libhtml-tagset-perl libhttp-date-perl
libhttp-message-perl libio-html-perl libio-string-perl liblwp-mediatypes-perl libparse-debianchangelog-perl libsigc++-2.0-0v5 libsqlite3-0
libsub-name-perl libtimedate-perl liburi-perl libxapian30
0 upgraded, 25 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/6000 kB of archives.
After this operation, 21.7 MB of additional disk space will be used.
ベストアンサー1
この質問は、これがext4またはXFSには役に立たないことを示しています。
また、より大きなパッケージ(linux-image-4.9.0-9-amd64
)のインストールもテストしました。それにもかかわらず、まだ同じ時間がかかるようです--force-unsafe-io
。
外部2
ext2では、--force-unsafe-io
インストール時間がlinux-image
50秒から13秒に短縮されました。
私がテストを実行しているカーネルは5.0.17-200.fc29.x86_64
ですCONFIG_EXT4_USE_FOR_EXT2
。
ユーザー空間aio_fsync()実装を使用してext2をテストしました。ただし、最良の改善はAIO fsync()の使用には依存しません。
私の進展は実際には副作用によるものでした。すべてのfsync()操作を最初に実行し、次にすべてのrename()操作を実行するようにdpkgを変更しました。パッチされていないdpkgはfsync()ごとにrename()を呼び出します。私が使用しているAIOのキューの深さは最大256です。キューの深さが1のAIO fsync()は、同期fsync()よりもかなり遅いです。若干のオーバーヘッドがあるようです。最良の改善のためには、SYNC_FILE_RANGE_WRITE
最初にすべての作業を完了する必要があります。改善されたバージョンのインストール時間はlinux-image
約18秒程度です。
この作業の順序は、実際にTed T'soによって最初に提案されたものです:-D。CONFIG_EXT4_USE_FOR_EXT2
fsync()は親ディレクトリも同期するのに役立ちます。すべてのファイル名操作を最初に実行して、ディレクトリごとに複数のディスクを更新しないようにします。 CONFIG_EXT2
以前の実装や通常のファイルシステムではこれは起こらないようですext4
。
ext4:今回は、fsyncにログなしで親ディレクトリを同期させます。
[...] 明らかに、これには ext2 基本モードも含まれます。 [...]
https://elixir.bootlin.com/linux/v5.0.17/source/fs/ext4/fsync.c#L38
* If we're not journaling and this is a just-created file, we have to
* sync our parent directory (if it was freshly created) since
* otherwise it will only be written by writeback, leaving a huge
* window during which a crash may lose the file. This may apply for
* the parent directory's parent as well, and so on recursively, if
* they are also freshly created.
以前と同様に、fsync() ステップを sync() に置き換えると、--force-unsafe-io
:-) と一致して驚くほど良いパフォーマンスを提供するようです。 sync() や syncfs() を使うことができればとても良いようです。
BTFS
btrfsでaio_fsync()テストを開始したとき、最近のデータ整合性の変更によってfsync()操作がファイルのrename()をブロックする可能性があることがわかりました。私はbtrfsに興味がないと決めました。