GNU Parallel并行加速脚本运行
GNU Parallel是xargs和shell循环的替代品,可以利用多核CPU并行执行任务(jobs),加快命令行脚本的运行速度。这里的任务(job)可以是单个命令或者一个shell小脚本。并行执行有一个任务池,将任务池中任务分发给每个进程单独执行,这里的任务池指输入源列表。一般输入源包括文件列表、主机列表、用户列表等等,每一个列表项就是一个任务的参数。本质上,parallel工具是一个perl脚本。
安装
如果机器上没有parallel命令,可以自行安装。
$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \ fetch -o - http://pi.dk/3 ) > install.sh
$ bash install.sh
并行执行
paralle可以替代xargs -P功能
# sleep 1+2+3+4+5 = 15秒
$ time echo {1..5} | xargs -n 1 sleep
#使用5个进程,最长的需要5秒
$ time echo {1..5} | xargs -n 1 -P 5 sleep # -n接受最多参数,-P最大进程数
# 以上命令可用paralle代替
$ time parallel sleep {} ::: {1..5}
{}
是参数占位符,可以省略,具体参数来自于后面的:::
输入源列表。
我们看看parallel执行情况。
$ parallel sleep {} ::: {30..40}
$ ps aux | grep sleep
从下图可以看出paralle命令根据输入的参数列表启动了多个进程运行,并且可以看出paralle命令实际上是一个perl脚本。
输入源
parallel可以从文件、命令行、stdin、管道读入参数列表。
$ parallel echo ::: A B C # 命令行输入
$ parallel -a abc-file echo # 文件
$ cat abc-file | parallel echo # 管道
多个输入源
如果命令需要多个参数,可以使用:::
连接多组输入源,不同输入源两两组合。
$ parallel echo ::: A B C ::: D E F # 多组输入源组合
A D
A E
A F
B D
B E
B F
C D
C E
C F
$ parallel -a abc-file -a def-file echo
$ cat abc-file | parallel -a - -a def-file echo #使用 - 组合
$ cat abc-file | parallel echo :::: - def-file # ::::代替-
命令可以是bash函数
# only works in bash
my_func() {
echo in my_func $1
}
export -f my_func
parallel my_func ::: 1 2 3
替换字符串
预定义的替换字符串操作,可以用于路径文件名处理
$ parallel echo ::: A/B.C #不改变字符串,等价于{}
A/B.C
$ parallel echo {} ::: A/B.C #缺省
A/B.C
$ parallel echo {.} ::: A/B.C #去除扩展名
A/B
$ parallel echo {/} ::: A/B.C #去除路径
B.C
$ parallel echo {//} ::: A/B.C #保留路径
A
$ parallel echo {/.} ::: A/B.C #去除路径和扩展
B
$ parallel echo {#} ::: A B C #给出任务号,从1到任务总数
1
2
3
$ parallel echo {2} {1} ::: A B C ::: D E F # {n} 第n组输入源
$ parallel echo {2} ::: A B C ::: D E F
$ seq 5 | parallel echo placeholder_{}
控制并行任务数
默认情况下,paralle命令会自动侦测到节点可用CPU核数,并且尽可能多的使用CPU核并发运行作业。
比如测试的节点有32个CPU核,
$ parallel echo {%} ::: {1..33}
1
2
3
...
31
32
1
使用{%}
输出job slot number,不同于任务号,根据同时并发运行任务数,将所有任务分配到每一个slot槽中并分配一个序号。如果任务总数超过允许的同时并发数目,一个slot中会分配超过一个任务数。上面例子中由于使用节点只有32个核,最多并发运行32个作业,因此slot number在1-32之间。
使用--jobs
或者-j
控制允许同时运行的任务数。
$ parallel --jobs 4 echo {%} ::: {1..33}
1
2
3
4
1
2
3
4
...
$ parallel --jobs 2 "sleep {}; echo {}" ::: 5 4 3 2 1
查看执行命令但不实际执行
使用--dryrun
查看执行命令,但不实际执行,常用于开发验证阶段。
$ parallel --dryrun echo {2} {1} ::: bird flower fish ::: Red Green Blue
echo Red bird
echo Green bird
echo Blue bird
echo Red flower
echo Green flower
echo Blue flower
echo Red fish
echo Green fish
echo Blue fish
{2} {1}
指定输入源顺序,第二组输入在前。
控制输出顺序
--keep-order
或者-k
控制输出顺序与输入顺序相同。默认是乱序的,按照命令执行快慢。
$ parallel --keep-order --tag "sleep {}; echo {}" ::: 5 4 3 2 1
管道模式
# 大文件切割多个块并行处理
$ cat bigfile | parallel --pipe wc -l
$ parallel --pipepart -a bigfile --block -l --recstart ">" grep foobar
循环并行
parallel命令代替shell循环for/while。下面示例中括号()
中执行for或者while循环,是将命令放在子shell中执行。加括号执行好处是命令组合更加清晰,同时限定变量的作用范围。
# Standard for-loop that does something with each line in a file called 'list'
(for x in `cat list` ; do
do_something $x
done) | process_output
# Standard while-read-loop that does something with each line in a file called 'list'
cat list | (while read x ; do
do_something $x
done) | process_output
# Converted into GNU parallel command
cat list | parallel do_something | process_output
实例
wgrib2裁剪小区域grib数据
- 生成文件列表再通过管道并行执行
#!/bin/bash #生成文件列表
for file in `ls 2022072600/*.grb2`
do
echo $file >> fnames.txt
done
# 使用4个进程并行执行
cat fnames.txt | parallel --jobs 4 wgrib2 {} -small_grib 100:120 30:50 result/{/.}_new.grb2 # {/.}去除路径和后缀名
- 可以不用临时文件fnames.txt
$ ls 2022072600/*grb2 | parallel --jobs 4 wgrib2 {} -small_grib 100:120 30:50 result/{/.}_new.grb2
或者
$ find 2022072600 -name "*.grb2" -type f | parallel --jobs 4 wgrib2 {} -small_grib 100:120 30:50 result/{/.}_new.grb2
参考资料
parallel_tutorial.pdf (gnu.org)

