GNU 并行和脚本未启动
GNU parallel and script not starting
我正在写一篇关于一些生物信息学工作的学术论文(我会按照作者的要求引用它;)),我需要加快我的 bash.
它基本上是一个 bash
脚本,它运行一个循环来迭代文件并查找带有 awk
的字符串。
我按照手册使用 parallel -a ./script.sh
。我遇到了变量问题,所以我将其更改为 -q
,但似乎脚本根本没有启动,尽管我没有收到错误消息。
我可能做错了什么,但我不明白是什么。以前,我不得不用 ::: 管道它,因为我有一个输入文件,但这个脚本没有任何。
剧本:
#!/bin/bash
files_chrM_ID="concat_chrM_*"
bam_directory="../bam/"
for ID_file in ${files_chrM_ID}
do
echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
echo "$(date +%H:%I:%S) $sample is being treated"
for bam_file_target in "${bam_directory}"*"${sample}"*".bam"
do
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target | awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=10=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
done
done
和我的命令:
parallel -q ./script.sh
GNU Parallel 并不神奇:您不能告诉它并行化任何脚本。
相反,您需要告诉它并行化什么以及如何并行化。
一般来说,您需要考虑必须并行生成您想要的命令列表 运行,然后将此列表提供给 GNU Parallel。
在脚本中有 2 个 for
循环和一个管道。所有这三个都可以使用 GNU Parallel 并行化。但是,不确定它是否有意义:并行化会产生开销,如果当前实现以最佳方式利用 CPU 和磁盘资源,那么并行化不会带来加速。
像这样的for
循环
for x in x-value1 x-value2 x-value3 ... x-valueN; do
# do something to $x
done
并行化为:
myfunc() {
x=""
# do something to $x
}
export -f myfunc
parallel myfunc ::: x-value1 x-value2 x-value3 ... x-valueN
A | B | C
形式的管道,其中 B
较慢,并行化为:
A | parallel --pipe B | C
因此,首先要确定瓶颈。
为此top
真的很有用。如果您在 top
中看到单个进程 运行ning 100%,那么它很适合并行化。
如果不是,那么您可能会受到磁盘速度的限制,而 GNU Parallel 很少能加速。
您没有包含测试数据,所以我无法 运行 您的脚本并为您确定瓶颈。但我有使用 samtools
的经验,而 samtools view
一直是我脚本中的瓶颈。所以让我们假设这里也是这种情况。
samtools ... | awk ...
这不适合 A | B | C
模板,其中 B
很慢,因此我们不能使用 parallel --pipe
来加快速度。但是,如果 awk
是瓶颈,那么我们 可以 使用 parallel --pipe
.
所以让我们看看两个 for
循环。
很容易并行化外循环:
#!/bin/bash
files_chrM_ID="concat_chrM_*"
do_chrM() {
ID_file=""
bam_directory="../bam/"
echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
echo "$(date +%H:%I:%S) $sample is being treated"
for bam_file_target in "${bam_directory}"*"${sample}"*".bam"
do
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target | awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=14=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
done
}
export -f do_chrM
parallel do_chrM ::: ${files_chrM_ID}
如果线程数 ${files_chrM_ID}
多于线程数 CPU,那就太好了。但如果不是这样,我们还需要并行化内部循环。
这有点棘手,因为我们需要导出一些变量以使它们对 do_bam
可见,parallel
调用:
#!/bin/bash
files_chrM_ID="concat_chrM_*"
do_chrM() {
ID_file=""
bam_directory="../bam/"
echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
# We need to export $sample and $ID_file to make them visible to do_bam()
export sample
export ID_file
echo "$(date +%H:%I:%S) $sample is being treated"
do_bam() {
bam_file_target=""
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target |
awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=15=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
}
export -f do_bam
parallel do_bam ::: "${bam_directory}"*"${sample}"*".bam"
}
export -f do_chrM
parallel do_chrM ::: ${files_chrM_ID}
然而,这可能会使您的服务器过载:内部并行不与外部并行通信,因此如果您 运行 在 64 核机器上这样做,您可能会面临 运行 64*64 作业的风险并行(但前提是有足够的文件匹配 concat_chrM_*
和 "${bam_directory}"*"${sample}"*".bam"
)。
在这种情况下,将外部 parallel
限制为 1 或 2 个并行作业是有意义的:
parallel -j2 do_chrM ::: ${files_chrM_ID}
这将在 64 核机器上最多 运行 2*64 个并行作业。
但是,如果您想一直运行 64 个作业并行,那么它就变得相当棘手了:如果内循环的值不依赖于外循环,因为那样你就可以简单地做类似的事情:
parallel do_stuff ::: chrM_1 ... chrM_100 ::: bam1.bam ... bam100.bam
这将并行生成 chrM_X、bamY.bam 和 运行 的所有组合 - 在 64 核机器上一次 64 个。
但在您的情况下,内循环中的值 do 取决于外循环中的值。这意味着您需要在开始任何作业之前计算这些值。这也意味着你不能在外循环中有你的脚本输出信息。
#!/bin/bash
sam_awk() {
bam_file_target=""
sample=""
ID_File=""
echo "$(date +%H:%I:%S) $ID_file is being treated"
echo "$(date +%H:%I:%S) $sample is being treated"
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target |
awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=18=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
}
files_chrM_ID="concat_chrM_*"
bam_directory="../bam/"
for ID_file in ${files_chrM_ID}
do
# Moved to inner
# echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
# Moved to inner
# echo "$(date +%H:%I:%S) $sample is being treated"
for bam_file_target in "${bam_directory}"*"${sample}"*".bam"
do
echo "$bam_file_target"
echo "$sample"
echo "$ID_File"
done
done | parallel -n3 sam_awk
鉴于你没有给我们任何测试数据,我无法测试这些脚本是否真的会运行,因此可能存在错误。
如果您还没有这样做,请至少阅读“GNU Parallel 2018”的第 1+2 章(可在
http://www.lulu.com/shop/ole-tange/gnu-parallel-2018/paperback/product-23558902.html 或
下载地址:https://doi.org/10.5281/zenodo.1146014)
只需不到 20 分钟,您的命令行就会爱上它。
我正在写一篇关于一些生物信息学工作的学术论文(我会按照作者的要求引用它;)),我需要加快我的 bash.
它基本上是一个 bash
脚本,它运行一个循环来迭代文件并查找带有 awk
的字符串。
我按照手册使用 parallel -a ./script.sh
。我遇到了变量问题,所以我将其更改为 -q
,但似乎脚本根本没有启动,尽管我没有收到错误消息。
我可能做错了什么,但我不明白是什么。以前,我不得不用 ::: 管道它,因为我有一个输入文件,但这个脚本没有任何。
剧本:
#!/bin/bash
files_chrM_ID="concat_chrM_*"
bam_directory="../bam/"
for ID_file in ${files_chrM_ID}
do
echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
echo "$(date +%H:%I:%S) $sample is being treated"
for bam_file_target in "${bam_directory}"*"${sample}"*".bam"
do
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target | awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=10=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
done
done
和我的命令:
parallel -q ./script.sh
GNU Parallel 并不神奇:您不能告诉它并行化任何脚本。
相反,您需要告诉它并行化什么以及如何并行化。
一般来说,您需要考虑必须并行生成您想要的命令列表 运行,然后将此列表提供给 GNU Parallel。
在脚本中有 2 个 for
循环和一个管道。所有这三个都可以使用 GNU Parallel 并行化。但是,不确定它是否有意义:并行化会产生开销,如果当前实现以最佳方式利用 CPU 和磁盘资源,那么并行化不会带来加速。
像这样的for
循环
for x in x-value1 x-value2 x-value3 ... x-valueN; do
# do something to $x
done
并行化为:
myfunc() {
x=""
# do something to $x
}
export -f myfunc
parallel myfunc ::: x-value1 x-value2 x-value3 ... x-valueN
A | B | C
形式的管道,其中 B
较慢,并行化为:
A | parallel --pipe B | C
因此,首先要确定瓶颈。
为此top
真的很有用。如果您在 top
中看到单个进程 运行ning 100%,那么它很适合并行化。
如果不是,那么您可能会受到磁盘速度的限制,而 GNU Parallel 很少能加速。
您没有包含测试数据,所以我无法 运行 您的脚本并为您确定瓶颈。但我有使用 samtools
的经验,而 samtools view
一直是我脚本中的瓶颈。所以让我们假设这里也是这种情况。
samtools ... | awk ...
这不适合 A | B | C
模板,其中 B
很慢,因此我们不能使用 parallel --pipe
来加快速度。但是,如果 awk
是瓶颈,那么我们 可以 使用 parallel --pipe
.
所以让我们看看两个 for
循环。
很容易并行化外循环:
#!/bin/bash
files_chrM_ID="concat_chrM_*"
do_chrM() {
ID_file=""
bam_directory="../bam/"
echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
echo "$(date +%H:%I:%S) $sample is being treated"
for bam_file_target in "${bam_directory}"*"${sample}"*".bam"
do
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target | awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=14=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
done
}
export -f do_chrM
parallel do_chrM ::: ${files_chrM_ID}
如果线程数 ${files_chrM_ID}
多于线程数 CPU,那就太好了。但如果不是这样,我们还需要并行化内部循环。
这有点棘手,因为我们需要导出一些变量以使它们对 do_bam
可见,parallel
调用:
#!/bin/bash
files_chrM_ID="concat_chrM_*"
do_chrM() {
ID_file=""
bam_directory="../bam/"
echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
# We need to export $sample and $ID_file to make them visible to do_bam()
export sample
export ID_file
echo "$(date +%H:%I:%S) $sample is being treated"
do_bam() {
bam_file_target=""
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target |
awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=15=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
}
export -f do_bam
parallel do_bam ::: "${bam_directory}"*"${sample}"*".bam"
}
export -f do_chrM
parallel do_chrM ::: ${files_chrM_ID}
然而,这可能会使您的服务器过载:内部并行不与外部并行通信,因此如果您 运行 在 64 核机器上这样做,您可能会面临 运行 64*64 作业的风险并行(但前提是有足够的文件匹配 concat_chrM_*
和 "${bam_directory}"*"${sample}"*".bam"
)。
在这种情况下,将外部 parallel
限制为 1 或 2 个并行作业是有意义的:
parallel -j2 do_chrM ::: ${files_chrM_ID}
这将在 64 核机器上最多 运行 2*64 个并行作业。
但是,如果您想一直运行 64 个作业并行,那么它就变得相当棘手了:如果内循环的值不依赖于外循环,因为那样你就可以简单地做类似的事情:
parallel do_stuff ::: chrM_1 ... chrM_100 ::: bam1.bam ... bam100.bam
这将并行生成 chrM_X、bamY.bam 和 运行 的所有组合 - 在 64 核机器上一次 64 个。
但在您的情况下,内循环中的值 do 取决于外循环中的值。这意味着您需要在开始任何作业之前计算这些值。这也意味着你不能在外循环中有你的脚本输出信息。
#!/bin/bash
sam_awk() {
bam_file_target=""
sample=""
ID_File=""
echo "$(date +%H:%I:%S) $ID_file is being treated"
echo "$(date +%H:%I:%S) $sample is being treated"
echo $bam_file_target // $sample
out_file=${ID_file:0:-4}_ON_${bam_file_target:8:-4}.sam
echo "$out_file will be created"
echo "samtools and awk starting"
samtools view -@ 6 $bam_file_target |
awk -v st="$ID_file" 'BEGIN {OFS="\t";ORS="\r\n"; while (getline < st) {st_array[]=}} {if ( in st_array) {print [=18=], st_array[], "target"}}' >> $out_file
echo "$out_file done."
}
files_chrM_ID="concat_chrM_*"
bam_directory="../bam/"
for ID_file in ${files_chrM_ID}
do
# Moved to inner
# echo "$(date +%H:%I:%S) $ID_file is being treated"
sample=${ID_file: -12}
sample=${sample:0:8}
# Moved to inner
# echo "$(date +%H:%I:%S) $sample is being treated"
for bam_file_target in "${bam_directory}"*"${sample}"*".bam"
do
echo "$bam_file_target"
echo "$sample"
echo "$ID_File"
done
done | parallel -n3 sam_awk
鉴于你没有给我们任何测试数据,我无法测试这些脚本是否真的会运行,因此可能存在错误。
如果您还没有这样做,请至少阅读“GNU Parallel 2018”的第 1+2 章(可在 http://www.lulu.com/shop/ole-tange/gnu-parallel-2018/paperback/product-23558902.html 或 下载地址:https://doi.org/10.5281/zenodo.1146014)
只需不到 20 分钟,您的命令行就会爱上它。