web-dev-qa-db-ja.com

1000000個の小さなファイルのコピーを高速化

ディレクトリに1000000 4-20 kbのファイルがあります。そのディレクトリをコピーする必要があります。しかし、私は各ファイルをシークする必要があるようですので、これにはかなり時間がかかります。

これをスピードアップできる方法はありますか?

これらのファイルが占めるディスクブロックを取得できたら、それらを並べ替え、近いブロックをマージし(シーケンシャルリードがシークよりも高速であることが多い場合)、これらのブロックを読み取って= RAMキャッシュ(32 GBのRAMがあります)をコピーする前にキャッシュします。

しかし、そのためには、ファイルがどのブロックにあるかを特定する方法が必要です。

磁気デバイス(SSDではない)でEXT4を使用しています。

編集:

これは機能するはずですが、機能しません。

ls |
parallel -IOO --pipe "Sudo parallel -j100 hdparm --fibmap {}'|tail -n +5'" |
sort -nk 2 | 
Perl -ane 'if($u+10000 < $F[1]) { print "$l ",($u-$l),"\n"; $l=$F[1] } $u=$F[2]' |
Sudo parallel --colsep ' ' dd if=/dev/sda1 skip={1} bs=512 count={2} '| cat >/dev/null'

大きなファイルでテストする場合、ファイルはキャッシュされません。

Edit2:

ここにいくつかのベンチマークがあります。各実行の間にキャッシュがフラッシュされました(echo 3 >/proc/sys/vm/drop_caches)。 iostats -dkx 5で測定。

rsync -Hav foo/ bar/: 1800 KB/s
cp -a foo/ bar/: 3600 KB/s
cat sort-by-inode | parallel -j1 -X cp foo/{} bar/: 5000 KB/s
cat sort-by-inode | shuf | parallel -j1 -X cp foo/{} bar/: 3000 KB/s
cat sort-by-inode | shuf | parallel -j10 -X cp foo/{} bar/: 7000 KB/s
cat sort-by-inode | parallel -j10 -X cp foo/{} bar/: 8000 KB/s
cat sort-by-inode | parallel -j100 -X cp foo/{} bar/: 9000 KB/s
cat sort-by-inode | parallel -j500 -X cp foo/{} bar/: 10000 KB/s

それから、私たちはそれから何を学ぶことができますか?

Iノードによるソートは良い考えのようです。しかし、複数のcpを並列化すると、パフォーマンスがさらに向上するようです。ソースfoo/が磁気ディスクであることは強調する価値があります。これにより、I/Oを単一のスピンドルに並列化してもI/Oが高速化しないという神話を攻撃します。ここでの並列化は明確かつ一貫して高速化されます。

10
Ole Tange

仮定して

  • readdirによって返されるエントリは、iノード番号で並べ替えられていません
  • iノード順にファイルを読み取ると、シーク操作の数が減ります。
  • ほとんどのファイルの内容は、最初の8k割り当て(ext4最適化)にあり、シーク操作も少なくなります。

iノード順にファイルをコピーすることで、コピーの高速化を試みることができます。

つまり、次のようなものを使用します。

$ cd /mnt/src
$ ls -U -i | sort -k1,1 -n | cut -d' ' -f2- > ~/clist
$ xargs cp -t /mnt2/dst < ~/clist
9
maxschlepzig

GNU tar-paxの伝統-ハードリンクを単独で処理します。

cd "$srcdir" ; tar --hard-dereference -cf - ./* |
    tar -C"${tgtdir}" -vxf -

そうすれば、2つのtarプロセスだけがあり、cpを何度も何度も呼び出す必要がなくなります。

4
mikeserv

@ maxschlepzig's の回答と同様に、filefragの出力を解析して、ディスクに最初のフラグメントが出現する順序でファイルを並べ替えることができます。

find . -maxdepth 1 -type f |
  xargs -d'\n' filefrag -v |
  sed -n '
    /^   0:        0../ {
      s/^.\{28\}\([0-9][0-9]*\).*/\1/
      h
      }
    / found$/ {
      s/:[^:]*$//
      H
      g
      s/\n/ /p
      }' |
    sort -nk 1,1 |
    cut -d' ' -f 2- |
    cpio -p dest_dir

上記のsedスクリプトを使用したMMVなので、十分にテストしてください。

それ以外の場合、何をしても、filefrage2fsprogsの一部)は、複数のファイル引数を取ることができるため、hdparmよりもはるかに高速に使用できます。 hdparm 1,000,000回実行するだけのオーバーヘッドは、多くのオーバーヘッドを追加します。

また、Perlスクリプト(またはCプログラム)を各ファイルのFIEMAPioctlに書き込んで、ブロックの並べ替えられた配列を作成することは、おそらくそれほど難しくありません。コピーする必要のあるファイルとファイルが属し、対応するファイルから各ブロックのサイズを読み取ることですべてを順番にコピーします(ただし、ファイル記述子が不足しないように注意してください)。

2
Graeme