我的目录中有 1000000 个 4-20 kb 文件(您可以生成类似的文件,如下所示seq 10000 | gzip > a; seq 1000000 | parallel --bar 'head -c{=$_=int(rand()*16)+4=}k a > {}'
:)
。我需要复制该目录。但似乎我必须对每个文件进行搜索,因此这需要相当长的时间。
有什么方法可以加快速度吗?
我目前正在考虑,如果我可以获得这些文件占用的磁盘块,我可以对它们进行排序,合并接近的块(考虑到顺序读取通常比查找更快)并读取这些块,以便它们位于 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'
在大文件上测试它时,它不会缓存该文件。
编辑2:
以下是一些基准。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
那么我们可以从中学到什么呢?
看来按 inode 排序是个好主意。但并行化似乎cp
可以进一步提升性能。值得强调的是,源foo/
是磁盘,因此这打破了将 I/O 并行到单个主轴不会加速 I/O 的神话:明确且一致地并行化可以加速此处的复制。
答案1
假如说
- 返回的条目
readdir
不按 inode 号排序 - 按 inode 顺序读取文件减少了查找操作的次数
- 大多数文件的内容都在初始 8k 分配中(ext4 优化),这也应该产生更少的查找操作
您可以尝试通过按 inode 顺序复制文件来加快复制速度。
这意味着使用这样的东西:
$ cd /mnt/src
$ ls -U -i | sort -k1,1 -n | cut -d' ' -f2- > ~/clist
$ xargs cp -t /mnt2/dst < ~/clist
答案2
tar
按照传统, GNUpax
自行处理硬链接。
cd "$srcdir" ; tar --hard-dereference -cf - ./* |
tar -C"${tgtdir}" -vxf -
这样你就只有两个tar
进程,并且不需要cp
一遍又一遍地调用。
答案3
类似地@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
MMV 带有上面的sed
脚本,所以一定要彻底测试。
否则,无论您做什么,filefrag
( 的一部分)的使用速度都会比它可以采用多个文件参数e2fsprogs
快得多。hdparm
仅仅运行 1,000,000 次的开销hdparm
就会增加很多开销。
perl
此外,编写一个脚本(或 C 程序),为FIEMAP
ioctl
每个文件创建一个应复制的块和所属文件的排序数组,然后按顺序复制所有内容可能不会那么困难从相应的文件中读取每个块的大小(但要注意不要用完文件描述符)。