Bash 合并数百万个文件的最快方法
远程计算机上有8100万个文件(!)存储在一个目录中。所有文件都以“.paintedHaploDiversity”结尾。我想将这些文件合并到父目录中名为Bash 合并数百万个文件的最快方法,bash,performance,file,merge,disk-io,Bash,Performance,File,Merge,Disk Io,远程计算机上有8100万个文件(!)存储在一个目录中。所有文件都以“.paintedHaploDiversity”结尾。我想将这些文件合并到父目录中名为alloutput_3.5的文件中。更具体地说,每个文件包含两行或三行。第一行是我可以忽略的标题。在剩下的一行或两行中,有一行在第四列中具有值2。对于每个文件,我希望复制第二列中有2的整行,并将文件名添加到其中(不包括扩展名“.paintedHaploDiversity”)。我将此文件名称为“simID” 有关信息,远程计算机在MAC OS X
alloutput_3.5
的文件中。更具体地说,每个文件包含两行或三行。第一行是我可以忽略的标题。在剩下的一行或两行中,有一行在第四列中具有值2
。对于每个文件,我希望复制第二列中有2
的整行,并将文件名添加到其中(不包括扩展名“.paintedHaploDiversity”)。我将此文件名称为“simID”
有关信息,远程计算机在MAC OS X 10.11.6(15G22010)上运行。这是一个简单的destkop。因此,不涉及任何网络(除了我的ssh命令以到达远程机器之外)
我第一次试过
for f in *;
do
simID=${f%.paintedHaploDiversity}
awk -v simID=${simID} 'NR>1{if ($4==2) {printf simID"\t"; print}}' $f >> ../allOutputs_3.5
done
但是速度很慢。我估计所需的时间要几个月甚至几年!然后,我试着
awk 'FNR==1{simID=substr(FILENAME, 1, length(FILENAME)-22)}FNR>1{if ($4==2) {printf simID"\t"; print}}' * >> ../allOutputs
但它似乎并没有更快。作为速度测试,我还考虑了
find . -exec cat '{}' ';' > out
但它又是非常缓慢的。考虑到问题可能来自正则表达式扩展*
,我试图通过两个C风格的循环来重现每个文件的名称,从而循环遍历它们
for ((bigID=1; bigID <= 9 ;++bigID)); do
for ((rep=1; rep <= 9000000 ;++rep)); do
awk -v simID=3.5.${bigID}_${rep} 'NR>1{if ($4==2) {printf simID"\t"; print}}' 3.5.${bigID}_${rep}.paintedHaploDiversity >> ../allOutputs_3.5
done
done
这一过程现在预计需要大约两周的时间。这开始是合理的。我仍然想知道是什么原因导致这一进程如此缓慢,是否可以改进
我想瓶颈可能是磁盘IO。还是需要大量CPU时间的文件系统?是因为同一目录中有这么多文件,并且在循环的每次迭代中都需要搜索文件的二叉树,所以这个过程会这么慢吗?如何改进?我应该试着用C++编写这个过程吗?
如果有帮助的话,在最后一个命令(使用printf
和tail
的命令)运行时,输出top-o MEM
Processes: 254 total, 3 running, 12 stuck, 239 sleeping, 1721 threads 03:12:40
Load Avg: 2.04, 1.79, 1.60 CPU usage: 0.84% user, 4.33% sys, 94.81% idle
SharedLibs: 85M resident, 11M data, 10M linkedit.
MemRegions: 42324 total, 4006M resident, 63M private, 230M shared.
PhysMem: 14G used (2286M wired), 10G unused.
VM: 753G vsize, 535M framework vsize, 1206153(0) swapins, 2115303(0) swapouts.
Networks: packets: 413664671/284G in, 126210468/104G out.
Disks: 1539349069/12T read, 1401722156/7876G written.
PID COMMAND %CPU TIME #TH #WQ #PORTS MEM PURG CMPRS PGRP PPID STATE
0 kernel_task 42.1 1716 hrs 167/25 0 2- 1968M 0B 0B 0 0 running
366 SystemUIServ 0.4 24:42:03 5 2 345 1055M 0B 10M 366 1 sleeping
472 softwareupda 0.0 12:46:11 5 0 3760 340M 0B 18M 472 1 sleeping
54242 Sublime Text 0.0 03:55:44 12 0 237 233M 0B 68K 54242 1 sleeping
63 powerd 0.0 44:07:21 2 0 95 204M 0B 8932K 63 1 sleeping
34951 Finder 0.1 04:11:06 9 2 1665 166M 0B 68M 34951 1 sleeping
197 WindowServer 0.0 40:02:58 3 0 453 142M 0B 63M 197 1 sleeping
13248 Terminal 0.0 84:19.45 5 0 388 114M 0B 113M 13248 1 sleeping
29465 X11.bin 0.0 89:38.70 9 0 229 104M 0B 16M 29464 29464 sleeping
12372 system_insta 0.0 00:31.61 2 0 75 78M 0B 9996K 12372 1 sleeping
1588 sysmond 0.0 02:34:04 2 1 23 62M 0B 4536K 1588 1 sleeping
54245 plugin_host 0.0 00:03.88 5 0 56 51M 0B 0B 54242 54242 sleeping
554 spindump 0.0 00:36.51 2 1 164 44M 0B 33M 554 1 sleeping
20024 com.apple.GS 0.0 00:01.43 3 2 24 43M 0B 2200K 20024 1 sleeping
475 suhelperd 0.0 00:19.84 2 0 55 42M 0B 28M 475 1 sleeping
418 installd 0.0 01:21.89 2 0 69 40M 0B 12M 418 1 sleeping
57 fseventsd 0.1 13:03:20 10 0 241 39M 0B 2904K 57 1 sleeping
364 Dock 0.0 08:48.83 3 0 283 38M 0B 27M 364 1 sleeping
201 sandboxd 0.0 18:55.44 2 1 38 38M 0B 10M 201 1 sleeping
103 loginwindow 0.0 04:26.65 2 0 377 35M 0B 3400K 103 1 sleeping
897 systemstatsd 0.0 65:30.17 2 1 43 34M 0B 4928K 897 1 sleeping
367 fontd 0.0 11:35.30 2 0 77 32M 0B 5920K 367 1 sleeping
396 ScopedBookma 0.0 01:00.46 3 2 46 32M 0B 28M 396 1 sleeping
22752 cfbackd 0.4 32:18.73 9 1 84 30M 0B 0B 22752 1 sleeping
39760 Preview 0.0 00:03.75 3 0 209 29M 0B 0B 39760 1 sleeping
53 syslogd 0.0 05:33:59 4 3 186- 29M- 0B 1668K 53 1 sleeping
533 SmartDaemon 0.0 27:07.67 10 7 175 28M 128K 5192K 533 1 stuck
388 iconservices 0.0 00:08.85 2 1 66 27M 0B 157M 388 1 sleeping
7268 diskmanageme 0.0 00:40.14 888 0 8899 27M 0B 7352K 7268 1 sleeping
513 Notification 0.0 00:46.42 3 0 245 26M 0B 9852K 513 1 sleeping
83 opendirector 0.0 19:22:12 6 5 8827 26M 0B 2444K 83 1 sleeping
557 AppleSpell 0.0 03:12.61 2 0 57 26M 0B 10M 557 1 sleeping
422 com.apple.ge 0.0 01:50.41 5 0 83 25M 0B 1680K 422 1 sleeping
397 storeaccount 0.0 00:48.41 4 0 1333 21M 0B 2248K 397 1 sleeping
87 launchservic 0.0 64:26.85 3 2 306 20M 0B 5804K 87 1 sleeping
1 launchd 0.0 26:26:23 5 4 1802 20M 0B 6532K 1 0 stuck
222 taskgated 0.0 17:59:00 3 1 43 19M 0B 4528K 222 1 sleeping
54 UserEventAge 0.0 18:19.74 3 0 32605- 18M- 0B 2968K 54 1 sleeping
4527 com.apple.sp 0.0 00:13.01 2 0 48 17M 0B 7792K 4527 1 sleeping
79 coreduetd 0.0 05:40.06 2 0 95 17M 0B 4604K 79 1 sleepin
这是iostat的输出
disk0 disk1 disk2 cpu load average
KB/t tps MB/s KB/t tps MB/s KB/t tps MB/s us sy id 1m 5m 15m
7.19 152 1.07 8.10 0 0.00 8.22 0 0.00 15 50 35 1.68 1.74 1.59
例如: 考虑以下文件 文件0:
first second third fourth fifth
bbb a a 2 r
文件1:
first second third fourth fifth
f o o 2 o
文件2:
first second third fourth fifth
f r e 1 e
x xxx x 2 x
文件3:
first second third fourth fifth
a a a 2 a
预期产量为
file_0 bbb a a 2 r
file_1 f o o 2 o
file_2 x xxx x 2 x
file_3 a a a 2 a
难题。可能把你自己画到了一个角落里 如果即使是
find
命令花费的时间太长,除了打开、读取和关闭每个文件之外什么也不做,那么可能的瓶颈就是硬盘上的寻道时间。这通常约为10毫秒(),因此假设每个文件有一次搜索,那么对于8100万个文件,您将在大约10天内查看。由于文件系统(目录访问等)的原因,可能会有更多的寻道,但如果局部性好,每个寻道也可能更短
如果你能等这么久,我建议你把所有这些文件压缩成一个文件。这将花费很多时间,但之后您可以更快地处理数据集
如果压缩(或复制或访问)每个单独的文件是不可能的,那么解决方案可能是拍摄整个文件系统的映像(快照),并将其复制到更快的驱动器上。SSD的寻道时间约为0.1毫秒(),因此使用SSD只需两个多小时即可完成
更为核心的方法是编写直接在原始磁盘字节上运行的代码,实现文件系统的必要部分,并使用大内存缓冲区来避免磁盘查找。根据文件在磁盘上的分散方式,这可能会给您带来很大的加速,但当然,编写此程序是一项不平凡的工作。您可能可以处理对程序
grep
和sed
的两个单独调用。这应该很快。也许比自编的C程序还要快
cd dir_with_all_the_files
grep -rE '^([^ ]+ +){3}2 ' . |
sed -En 's/^\.\/(.*)\.paintedHaploDiversity:/\1 /p' > ../allOutputs_3.5
作出的假设:
- 要搜索的列的标题也不是
2
- 该目录不包含子目录。
该命令仍可能产生正确的结果,但必须运行不必要的时间 - 文件名不包含
或换行符:
- 您的
实现支持非Posixgrep
选项(通常情况下)-r
grep
实施支持进一步改进:
- 添加
以加快搜索速度-m1
- 请尝试
(Mac OS通常不支持)或grep-P
。PCRE有时更快。使用PCRE,您还可以尝试使用可选的regexpcregrep
'^(.*?{3}2'
(注意,--exclude dir\*
被引用)排除子目录,因此即使没有上述假设,您也可以使用该命令*
*.paintedhaplodivity
时得到的那样),请在之后运行sort-t'-k 1,1-o alloutput_3.5{,}
您不妨设置
export LC_ALL=C
以加快grep
,排序
,甚至sed
,除了处理几GB数据的明显I/O负载之外,问题是启动一个或多个进程需要花费8100万次的时间。即使创建命令行或将文件全局扩展到300MB(表示*.
)也可能需要大量时间或超出系统和程序规范
一种解决方案是编写一个C程序来打开文件并对其进行处理,或者将其内容传递给其他程序。但这可能需要几天来编程和调试,也许你的实习生正在休假。但是Unix工具箱中已经有一些程序可以完成您所需的部分工作,只是文件名丢失了。我们假设所有文件都在一个名为bla的目录中
使用tar创建包含文件内容的流,如下所示:
tar cf-bla | tar-xOf-
默认情况下,这会将文件的连接内容写入控制台的标准输出。TAR和grep都只是
cd dir_with_all_the_files
grep -rE '^([^ ]+ +){3}2 ' . |
sed -En 's/^\.\/(.*)\.paintedHaploDiversity:/\1 /p' > ../allOutputs_3.5
for bigID in {1..6}
do
# poll first 99 files (r=1..99) + 9 millionth file
awk 'FNR==1{simID=substr(FILENAME, 1, length(FILENAME)-22)}FNR>1{if ($4==2) {printf simID"\t"; print}}' 3.5_${bigID}_{1..99}.paintedHaploDiversity 3.5_${bigID}_9000000.paintedHaploDiversity >> ../allOutputs
# break rest of files into ~10K chunks based on first 3 digits of suffix
for r in {100..899} # break 9000000 into ~10K chunks
do
awk 'FNR==1{simID=substr(FILENAME, 1, length(FILENAME)-22)}FNR>1{if ($4==2) {printf simID"\t"; print}}' 3.5_${bigID}_${r}*.paintedHaploDiversity >> ../allOutputs
done
done
find . -type f -printf "%f\n" |\
xargs awk '$4==2{ print(substr(FILENAME, 1, length(FILENAME)-22), $0) }' >> output.txt
for (( i=1;i<=9;i++ )); do
for (( j=1;j<=9000000;j++ )); do
printf "file_%s_%s\n" "$i" "$j" >> filenames.txt
done
done
cat filenames.txt | xargs awk '{...}'
split -l 1000000 -d filenames.txt chunk
for f in chunk*; do cat "$f" | xargs awk '{...}' ; done