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


10

ディレクトリに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を高速化しないという神話を攻撃します。


ディレクトリにはファイルのみが含まれていますか?ターゲットの場所は同じファイルシステムにありますか?
ジョセフR.

コピーに使用している正確なコマンドは?のようなものcp -r /mnt/dir1 /mnt/dirdestまたはのようなものcp /mnt/dir1/* /mnt/dirdestですか?
maxschlepzig 2014

ファイルのみで同じファイルシステムではない:cp -r / mnt / dir1 / mnt2 / dirdest
Ole

ところで、ディスクシークを最小限にしたいときに、ハードディスクへのアクセスを並行して実行しても役に立たないと思います。
maxschlepzig 2014

私はここにエクステント番号(ディスク上で発生する可能性が高い)でファイルを並べ替える
nh2

回答:


9

仮定して

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

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

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

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

@mikeserv、どういう意味ですか?ls -Uiノード番号でソートしないので十分ではありません...そしてなぜ私は欲しいの-1ですか?
maxschlepzig 2014

@mikeserv、「ディレクトリ順」はiノード順と同じではありません!この場合、別の単語を使用する必要はありません。あなたが奇妙だと思うことは関係ありません。ext4ファイルシステムでもテストしました。また、ディレクトリの順序は、実際にはiノードの順序とは異なります。-1「1行に1つのファイル」をリストするだけです-ファイル名の改行には役立ちません。そのために使用できますfind -print0/xargs -O
maxschlepzig 2014

@mikeserv、何のことを言ってるの?反例:mkdir tmp; cd tmp; touch foo"<RETURN>"bar; ls「foo?bar」を出力します。A ls -1は「foo?bar」も出力します。ls -1 | wc -l版画「2」。Aはfind -ls「./foo\nbar」としてファイル名を出力します。cp -i x「はディレクトリではありません『:LS -1` X 'は』ターゲットはCP」で失敗します。
maxschlepzig 2014

くそー、あなたは私に左と右を教えています!-q私が思っていた-1ことをします!繰り返しますが、私の謝罪-言うまでもありません。
mikeserv 2014

4

GNU tar-でpax、独自のハンドルのハードリンク-伝統。

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

そうすれば、2つのtarプロセスのみが実行さcpれ、何度も何度も呼び出す必要がなくなります。


2

@maxschlepzigの答えと同様に、の出力を解析し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 なので、十分にテストしてください。

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

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


これは素晴らしいです。home.ifi.uio.no/ paalh / publications / files / ipccc09.pdfも参照してください。このアプローチを説明tarし、ファイルの最大 4倍のスピードアップを示した論文があります。
nh2

1
論文の著者にメールを送り、qtarオープンソースとしてリリースできるかどうか尋ねた。現在はgithub.com/chlunde/qtarにあります
nh2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.