有口碑的南通网站建设,wordpress页面透明度,东莞网站开发公司哪家好,微信公众号怎么做微网站Bash命令通常单线程运行#xff0c;这意味着所有的处理工作只在单个 CPU 上执行。随着 CPU 规模的扩大以及核心数目的增加#xff0c;这意味着只有一小部分的 CPU 资源用于处理任务#xff0c;这样就造成了很大的资源浪费。 这种情况在进行多媒体转换(比如#xff1a;图片和…Bash命令通常单线程运行这意味着所有的处理工作只在单个 CPU 上执行。随着 CPU 规模的扩大以及核心数目的增加这意味着只有一小部分的 CPU 资源用于处理任务这样就造成了很大的资源浪费。 这种情况在进行多媒体转换(比如图片和视频转换)以及数据压缩中经常遇到。本文我们将介绍如何使用 GNU Parallel 程序在所有 CPU 核上并行地执行计算任务。Parallel 介绍GNU Parallel 是一种通用的并行化程序可以在同一台机器上或在您具有 SSH 访问权限的多台机器上轻松并行运行作业。如果要在 4 个 CPU 上运行 32 个不同的作业并行化的一种直接方法是在每个 CPU 上运行 8 个作业。GNU Parallel 会在完成后生成一个新进程并保持 CPU 处于活动状态从而节省时间。Parallel 安装1. 通过包安装•CentOS / RHEL$ yum install parallel•Ubuntu / Debian$ sudo apt install parallel2. 通过脚本安装$ (wget -O - pi.dk/3 || curl pi.dk/3/) | bashParallel 使用Parallel 语法简介Usage:parallel [options] [command [arguments]] list_of_argumentsparallel [options] [command [arguments]] (::: arguments|:::: argfile(s))...cat ... | parallel --pipe [options] [command [arguments]]•Parallel 常用选项::: 后面接参数。:::: 后面接文件。-j、--jobs 并行任务数不想并行执行可以设为 1。若不加 -j则预设为每个 CPU 执行一个 job。-N 每次输入的参数数量。-L N: 一次最多读取 N 行。--xargs 会在一行中输入尽可能多的参数。-xapply 从每一个源获取一个参数(或文件一行)。--header 把每一行输入中的第一个值做为参数名。-m 表示每个 job 不重复输出“背景”(context)。-X 与 -m 相反会重复输出“背景文本”。-q 保护后面的命令。--trim lr 去除参数两头的空格只能去除空格换行符和 tab 都不能去除。--keep-order/-k 强制使输出与参数保持顺序 --keep-order/-k。--tmpdir/ --results 都是保存文件但是后者可以有结构的保存。--delay 延迟每个任务启动时间。--halt 终止任务。--pipe 该参数使得我们可以将输入(stdin)分为多块(block)再将 stdin 的资料分给各个 jobs。--block 参数可以指定每块的大小。Parallel 用法简介1. 输入源GNU Parallel 的输入源支持文件、命令行和标准输入( Stdin 或 Pipe)。以命令行做为输入源。$ parallel echo ::: a b c d e | tee a.txtabcde以 Stdin(标准输入)作为输入源。$ cat a.txt | parallel echoabcdeGNU Parallel 支持通过命令行指定多个输入源它会生成所有的组合。$ parallel echo ::: A B C ::: D E F | tee b.txtA DA EA FB DB EB FC DC EC F多个文件作为输入此时多个文件中的内容也会像上面那样进行组合。$ parallel -a a.txt -a b.txt echoStdin(标准输入)作为文件源中的一个使用 - 输出结果同上。$ cat a.txt |parallel -a - -a b.txt echo使用 :::: 代替 -a后面可接多个文件名。$ cat a.txt | parallel echo :::: - b.txt::: 和 :::: 可以同时使用同样的输出结果也会进行组合。$ parallel echo ::: a b :::: b.txt当然若不想像上面那样进行组合可使用--xapply参数从每一个源获取一个参数(或文件一行)这个参数有些类似 R 中的函数具有广播作用。如果其中一个输入源的长度比较短它的值会被重复。$ parallel --xapply echo ::: A B C ::: D E FA DB EC F$ parallel --xapply echo ::: A B C ::: D E F G H IA DB EC FA GB HC I2. 改变参数分隔符GNU Parallel 可以通过 --arg-sep 和 --arg-file-sep 指定分隔符替代 ::: 或 ::::当这两个符号被其它命令占用的时候会特别有用。$parallel -k --arg-sep ,,, echo ,,, a b ,,, c d | tee c.txta ca db cb d$parallel --xapply --arg-file-sep ,,,, echo ,,,, a.txt b.txta A Db A Ec A Fd B De B Ea B Fb C Dc C Ed C F3. 改变输入分隔符GNU Parallel 默认把一行做为一个参数。使用 \n 做为参数定界符可以使用 -d 改变。$ parallel -d b echo :::: a.txtacde4. 提前结束和跳过空行GNU Parallel 支持通过 -E 参数指定一个值做为结束标志。$ parallel -E stop echo ::: A B stop C DABGNU Parallel 使用 --no-run-if-empty 来跳过空行。$ (echo 1; echo; echo 2) | parallel --no-run-if-empty echo125. 构建命令行如果 Parallel 之后没有给定命令那么这些参数会被当做命令。$ parallel ::: ls echo foo pwda.txtb.txtc.txtjianchenmypipescriptssnake_testWGS_snakefoo/home/sxuan此外命令还可以是一个脚本文件一个二进制可执行文件或一个Bash的函数(须用export -f导出函数)。$ echo echo \$* s.sh$ parallel ./s.sh ::: a b c f 1 2 3 4a b c f1 2 3 46. 替换字符串GNU Parallel 支持多种替换字符串默认使用 {}使用 -I 改变替换字符串符号 {}。其最常见的字符串替换包括以下几种•{.}去掉扩展名•{/},去掉路径只保留文件名•{//}只保留路径•{/.}同时去掉路径和扩展名•{#}输出任务编号同时对于每一个字符串替换都可以自己指定符号•-I 对应{}•--extensionreplace 替换 {.}•--basenamereplace 替换 {/}•--dirnamereplace 替换 {//}•--basenameextensionreplace 替换 {/.}•--seqreplace 替换 {#}$ parallel echo ::: A/B.C ; parallel echo {} ::: A/B.C ; parallel -I ,, echo ,, ::: A/B.CA/B.CA/B.CA/B.C$ parallel echo {.} ::: A/B.C ; parallel --extensionreplace ,, echo ,, ::: A/B.CA/BA/B$ parallel echo {/} ::: A/B.C ; parallel --basenamereplace ,, echo ,, ::: A/B.CB.CB.C$ parallel echo {//} ::: A/B.C ; parallel --dirnamereplace ,, echo ,, ::: A/B.CAA$ parallel echo {/.} ::: A/B.C ; parallel --basenameextensionreplace ,, echo ,, ::: A/B.CBB$ parallel echo {#} ::: A B C ; parallel --seqreplace ,, echo ,, ::: A B C123123同时如果有多个输入源时可以通过 {编号} 指定某一个输入源的参数。$ parallel --xapply echo {1} and {2} ::: A B ::: C DA and CB and D可以使用 / // /. 和 . 改变指定替换字符串。$ parallel echo /{1/} //{1//} /.{1/.} .{1.} ::: A/B.C D/E.F/B.C //A /.B .A/B/E.F //D /.E .D/E位置可以是负数表示倒着数。$ parallel echo 1{1} 2{2} 3{3} -1{-1} -2{-2} -3{-3} ::: A B ::: C D ::: E F1A 2C 3E -1E -2C -3A1A 2C 3F -1F -2C -3A1A 2D 3E -1E -2D -3A1A 2D 3F -1F -2D -3A1B 2C 3E -1E -2C -3B1B 2C 3F -1F -2C -3B1B 2D 3E -1E -2D -3B1B 2D 3F -1F -2D -3B7. 按列输入和指定参数名使用 --header 把每一行输入中的第一个值做为参数名。$ parallel --xapply --header : echo f1{f1} f2{f2} ::: f1 A B ::: f2 C D | tee d.txtf1A f2Cf1B f2D使用 --colsep 把文件中的行切分为列做为输入参数。$ perl -e printf f1\tf2\nA\tB\nC\tD\n tsv-file.tsv$ parallel --header : --colsep \t echo f1{f1} f2{f2} :::: tsv-file.tsv f1A f2Bf1C f2D8. 多参数--xargs 会在一行中输入尽可能多的参数(与参数字符串长度有关)通过 -s 可指定一行中参数的上限。$ perl -e for(1..30000){print $_\n} num30000$ cat num30000 | parallel --xargs echo | wc -l3$ cat num30000 | parallel --xargs -s 10000 echo | wc -l17为了获得更好的并发性GNU Parallel 会在文件读取结束后再分发参数。GNU Parallel 在读取完最后一个参数之后才开始第二个任务此时会把所有的参数平均分配到 4 个任务(如果指定了4个任务)。第一个任务与上面使用 --xargs 的例子一样但是第二个任务会被平均的分成 4 个任务最终一共 5 个任务。$ cat num30000 | parallel --jobs 4 -m echo | wc -l5将 1-10 分参数分配到4个任务可以看得更清晰。$ parallel --jobs 4 -m echo ::: {1..10}1 2 34 5 67 8 910替换字符串可以是输出字符的一部分使用 -m 参数表示每个 job 不重复输出 “背景”(context)-X 则与 -m 相反会重复输出 “背景文本”具体通过下面几个例子进行理解。$ parallel --jobs 4 echo pre-{}-post ::: A B C D E F Gpre-A-postpre-B-postpre-C-postpre-D-postpre-E-postpre-F-postpre-G-post$ parallel --jobs 4 -m echo pre-{}-post ::: A B C D E F Gpre-A B-postpre-C D-postpre-E F-postpre-G-post$ parallel --jobs 4 -X echo pre-{}-post ::: A B C D E F Gpre-A-post pre-B-postpre-C-post pre-D-postpre-E-post pre-F-postpre-G-post使用 -N 限制每行参数的个数其中 -N0 表示一次只读取一个参数且不输入这个参数(作为计数器来使用)。$ parallel -N4 echo 1{1} 2{2} 3{3} ::: A B C D E F G H1A 2B 3C1E 2F 3G$parallel -N0 echo foo ::: 1 2 3foofoofoo9. 引用如果命令行中包含特殊字符就需要使用引号保护起来。Perl 脚本 print ARGV\n 与 Linux 的 echo 的功能一样。$ perl -e print ARGV\n AA使用 GNU Parallel 运行这条命令的时候Perl 命令需要用引号包起来也可以使用 -q 保护 Perl 命令。$ parallel perl -e print ARGV\n ::: This wont work$parallel -q perl -e print ARGV\n ::: This worksThisworks$ parallel perl -e \print ARGV\n\ ::: This works, tooThisworks,too10. 去除空格使用--trim去除参数两头的空格。$ parallel --trim r echo pre-{}-post ::: A pre- A-post$ parallel --trim l echo pre-{}-post ::: A pre-A -post$ parallel --trim lr echo pre-{}-post ::: A pre-A-post11. 控制输出使用 --tag 以参数做为输出前缀使用 --tagstring 修改输出前缀。$ parallel --tag echo foo-{} ::: A B CA foo-AB foo-BC foo-C$ parallel --tagstring {}-bar echo foo-{} ::: A B CA-bar foo-AB-bar foo-BC-bar foo-C--dryrun 作用类似于 echo 。$ parallel --dryrun echo {} ::: A B Cecho Aecho Becho C$ parallel echo {} ::: A B CABC--verbose 则在运行之前先打印命令。$ parallel --verbose echo {} ::: A B Cecho Aecho Becho CABC一般来说GNU Parallel 会延迟输出直到一组命令执行完成。使用 --ungroup可立刻打印输出已完成部分。$ parallel -j2 printf %s-start\n%s {} {};sleep {};printf %s\n -middle;echo {}-end ::: 4 2 12-start2-middle2-end1-start1-middle1-end4-start4-middle4-end$ parallel -j2 --ungroup printf %s-start\n%s {} {};sleep {};printf %s\n -middle;echo {}-end ::: 4 2 14-start42-start2-middle2-end1-start1-middle1-end-middle4-end使用 --ungroup 会很快但会导致输出错乱一个任务的行输出可能会被另一个任务的输出截断。像上例所示第二行输出混合了两个任务4-middle 2-start。使用 --linebuffer 避免这个问题(稍慢一点)。4-start2-start2-middle2-end1-start1-middle1-end4-middle4-end强制使输出与参数保持顺序 --keep-order/-k。$ parallel -j2 -k printf %s-start\n%s {} {};sleep {};printf %s\n -middle;echo {}-end ::: 4 2 14-start4-middle4-end2-start2-middle2-end1-start1-middle1-end12. 将输出保存到文件GNU Parallel 可以把每一个任务的输出保存到文件中临时文件默认保存在 /tmp 中可以使用 --tmpdir 改变(或者修改 $TMPDIR)。$ parallel --files ::: A B C/tmp/parfmNTJ.par/tmp/parmioFz.par/tmp/pargaTxf.par$ parallel --tmpdir ~ --files ::: A B C/home/sxuan/parLEXH7.par/home/sxuan/parXsKsR.par/home/sxuan/parZxytI.par$ TMPDIR~ parallel --files ::: A B C/home/sxuan/par2tX6C.par/home/sxuan/parorPJy.par/home/sxuan/pari5TkI.par输出文件可以有结构的保存 --results输出文件不仅包含标准输出(stdout)也会包含标准错误输出(stderr)。$ parallel --results outdir echo ::: A B CABC$ tree outdir/outdir/└── 1├── A│ ├── seq│ ├── stderr│ └── stdout├── B│ ├── seq│ ├── stderr│ └── stdout└── C├── seq├── stderr└── stdout4 directories, 9 files在使用多个变量的时候会显得很有用。# --header : will take the first value as name and use that in the directory structure.$ parallel --header : --results outdir echo ::: f1 A B ::: f2 C DA CA DB CB D$ tree outdir/outdir/└── f1├── A│ └── f2│ ├── C│ │ ├── seq│ │ ├── stderr│ │ └── stdout│ └── D│ ├── seq│ ├── stderr│ └── stdout└── B└── f2├── C│ ├── seq│ ├── stderr│ └── stdout└── D├── seq├── stderr└── stdout9 directories, 12 files13. 控制执行使用 --jobs/-j 指定并行任务数。使用 64 个任务执行 128 个休眠命令。$ time parallel -N0 -j64 sleep 1 ::: {1..128}real 0m2.759suser 0m0.657ssys 0m1.345s默认情况下并行任务数与 Cpu 核心数相同, 所以这条命令会比每个 Cpu 两个任务的耗时多一倍。$ time parallel -N0 sleep 1 ::: {1..128}real 0m3.478suser 0m0.656ssys 0m1.344s# 每个 Cpu 两个任务$ time parallel -N0 --jobs 200% sleep 1 ::: {1..128}real 0m2.659suser 0m0.734ssys 0m1.423s使用 --jobs 0 表示执行尽可能多的并行任务。$ time parallel -N0 --jobs 0 sleep 1 ::: {1..128}real 0m2.135suser 0m0.651ssys 0m1.477s除了基于 Cpu 使用率之外也可以基于 Cpu 数。$ time parallel --use-cpus-instead-of-cores -N0 sleep 1 ::: {1..128}real 1m5.499suser 0m0.950ssys 0m1.897s14. 交互通过使用 --interactive 在一个任务执行之前让用户决定是否执行。$ parallel --interactive echo ::: 1 2 3echo 1 ?...yecho 2 ?...yecho 3 ?...y12315. 耗时当 job 有大量的 IO 操作时为避免“惊群效应”可使用 --delay 参数指定各个 job 开始的时间间隔。$ parallel --delay 2.5 echo Starting {}\;date ::: 1 2 3Starting 1Tue Apr 17 15:21:41 CST 2018Starting 2Tue Apr 17 15:21:44 CST 2018Starting 3Tue Apr 17 15:21:46 CST 2018若已知任务超过一定时间未反应则为失败则可以通过 --timeout 指定等待时间避免无谓的等待。GNU Parallel 能计算所有任务运行时间的中位数因此可以指定时间为中位数的倍数关系。$ parallel --timeout 4.1 sleep {}\; echo {} ::: 2 4 6 824$ parallel --timeout 200% sleep {}\; echo {} ::: 2.1 2.2 3 7 2.32.12.22.3316. 显示任务进度信息GNU Parallel 有多种方式可用来动态的显示任务进度信息如$ parallel --eta sleep ::: 1 3 2 2 1 3 3 2 1$ parallel --progress sleep ::: 1 3 2 2 1 3 3 2 1$ seq 1000 | parallel -j10 --bar (echo -n {};sleep 0.1) 2 (zenity --progress --auto-kill --auto-close)使用 --joblog 参数能够生成各个任务的日志文件。$ parallel --joblog /tmp/log exit ::: 1 2 3 0$ cat /tmp/log Seq Host Starttime JobRuntime Send Receive Exitval Signal Command1 : 1523950890.344 0.018 0 0 1 0 exit 12 : 1523950890.350 0.014 0 0 2 0 exit 23 : 1523950890.357 0.006 0 0 3 0 exit 34 : 1523950890.363 0.006 0 0 0 0 exit 0通过 --resume-failed 参数可以重新运行失败的任务。--retry-failed 的作用与 --resume-failed类似只是 --resume-failed 从命令行读取失败任务而 --retry-failed 则是从日志文件中读取失败任务。$ parallel --resume-failed --joblog /tmp/log exit ::: 1 2 3 0 0 0$ cat /tmp/logSeq Host Starttime JobRuntime Send Receive Exitval Signal Command1 : 1523950890.344 0.018 0 0 1 0 exit 12 : 1523950890.350 0.014 0 0 2 0 exit 23 : 1523950890.357 0.006 0 0 3 0 exit 34 : 1523950890.363 0.006 0 0 0 0 exit 01 : 1523951289.575 0.029 0 0 1 0 exit 12 : 1523951289.580 0.025 0 0 2 0 exit 23 : 1523951289.585 0.019 0 0 3 0 exit 35 : 1523951289.591 0.013 0 0 0 0 exit 06 : 1523951289.604 0.004 0 0 0 0 exit 0$ parallel --retry-failed --joblog /tmp/log$ cat /tmp/logSeq Host Starttime JobRuntime Send Receive Exitval Signal Command1 : 1523950890.344 0.018 0 0 1 0 exit 12 : 1523950890.350 0.014 0 0 2 0 exit 23 : 1523950890.357 0.006 0 0 3 0 exit 34 : 1523950890.363 0.006 0 0 0 0 exit 01 : 1523951289.575 0.029 0 0 1 0 exit 12 : 1523951289.580 0.025 0 0 2 0 exit 23 : 1523951289.585 0.019 0 0 3 0 exit 35 : 1523951289.591 0.013 0 0 0 0 exit 06 : 1523951289.604 0.004 0 0 0 0 exit 01 : 1523951445.089 0.013 0 0 1 0 exit 12 : 1523951445.094 0.009 0 0 2 0 exit 23 : 1523951445.102 0.007 0 0 3 0 exit 317. 终止任务GNU Parallel 支持在某一情况下(如第一个失败或成功时或者 20% 任务失败时)终止任务。终止任务又有两种类型•其一为立即终止(通过 --halt now 指定)杀死所有正在运行的任务并停止生成新的任务。•其二为稍后终止(通过 --halt soon 指定)停止生成新任务并等待正在运行任务完成。$ parallel -j2 --halt soon,fail1 echo {}\; exit {} ::: 0 0 1 2 3001parallel: This job failed:echo 1; exit 1parallel: Starting no more jobs. Waiting for 1 jobs to finish.2parallel: This job failed:echo 2; exit 2$ parallel -j2 --halt now,fail1 echo {}\; exit {} ::: 0 0 1 2 3001parallel: This job failed:echo 1; exit 1$ parallel -j2 --halt soon,fail20% echo {}\; exit {} ::: 0 1 2 3 4 5 6 7 8 901parallel: This job failed:echo 1; exit 12parallel: This job failed:echo 2; exit 2parallel: Starting no more jobs. Waiting for 1 jobs to finish.3parallel: This job failed:echo 3; exit 3$ parallel -j2 --halt now,success1 echo {}\; exit {} ::: 1 2 3 0 4 5 61230parallel: This job succeeded:echo 0; exit 0GNU Parallel 还支持在任务失败后重试运行 --retries。$ parallel -k --retries 3 echo tried {} /tmp/runs; echo completed {}; exit {} ::: 1 2 0completed 1completed 2completed 0$ cat /tmp/runstried 1tried 2tried 0tried 1tried 2tried 1tried 2关于终止信号的高级用法参考官方入门文档。18. 资源限制GNU Parallel 能够在开始一个新的任务前检查系统的负载情况防止过载(通过 --load 可指定负载)同时还能检查系统是否使用了交换空间 Swap(通过 --noswap 限制使用 Swap)。$ parallel --load 100% echo load is less than {} job per cpu ::: 1load is less than 1 job per cpu$ parallel --noswap echo the system is not swapping ::: nowthe system is not swapping now同时对于某些占用内存较多的程序Parallel 会检查内存只有内存满足时才启动任务(通过 --memfree 指定需要内存大小)而且在启动任务后内存不够 50% 时会杀掉最新开始的任务直到这个任务完成再重新开始那些杀死的任务。$ parallel --memfree 1G echo will run if more than 1 GB is ::: freewill run if more than 1 GB is free还可以通过 --nice 来指定任务的优先级。$ parallel --nice 17 echo this is being run with nice -n ::: 17this is being run with nice -n 1719. 远程操作可使用 -S host来进行远程登陆。$ parallel -S username$SERVER1 echo running on ::: username$SERVER120. 文件传输GNU Parallel 文件传输使用的是 Rsync。$ echo This is input_file input_file$ parallel -S $SERVER1 --transferfile {} cat ::: input_file更多远程操作参见官方入门文档。21. --pipe--pipe 参数使得我们可以将输入(stdin)分为多块(block)然后分配给多个任务多个 Cpu 以达到负载均衡最后的结果顺序与原始顺序一致。使用 --block 参数可以指定每块的大小默认为 1M。$ perl -e for(1..1000000){print $_\n} num1000000$ cat num1000000 | parallel --pipe wc 165668 165668 1048571 149796 149796 1048572 149796 149796 1048572 149796 149796 1048572 149796 149796 1048572 149796 149796 1048572 85352 85352 597465如果不关心结果顺序只想要快速的得到结果可使用 --round-robin 参数。没有这个参数时每块文件都会启动一个命令使用这个参数后会将这些文件块分配给 job 数任务(通过 --jobs 进行指定)。若想分配更为均匀还可同时指定 --block 参数。$ cat num1000000 | parallel --pipe -j4 --round-robin wc299592 299592 2097144315464 315464 2097143149796 149796 1048572235148 235148 1646037$ cat num1000000 | parallel --pipe -j4 --block 2M --round-robin wc299593 299593 2097151315465 315465 2097150299593 299593 209715185349 85349 597444Parallel 使用实例下面来看一个实际的使用 Parallel 的例子可能会更容易理解一些。•使用 Parallel 来进行 JPEG 压缩下面是一个普通的 find 命令用来找出当前目录中的所有 .jpg 文件然后通过 MozJPEG 包中提供的图像压缩工具 cjpeg 对其进行处理。$ find . -type f -name *.jpg -exec cjpeg -outfile LoRes/{} {} ;总共耗时 0m44.114s。从 top 运行结果可以看到虽然有 8 个核可用但实际只有单个线程在用单个核。下面用 Parallel 来运行相同的命令。$ find . -type f -name *.jpg | parallel cjpeg -outfile LoRes/{} {}这次压缩所有图像的时间缩减到了 0m10.814s。从 top 运行结果可以看到所有 CPU 核都满负荷运行有 8 个线程对应使用 8 个 CPU 核。参考文档https://www.google.comhttp://t.cn/E6KnjtPhttp://t.cn/E6KB5Rrhttp://t.cn/E69yv3ghttp://t.cn/Rm9X2WChttp://t.cn/E6Wr74r今日思想什么是成功?每个人眼里的成功都不一样。我认为成功不是别人觉得你成功就是成功成功是一种内心深处的自我感受。我不认为自己是成功者也不认为自己是失败者我只是在追求内心的一些东西在路上—— 雷军