如何加速使用多个大 gzip 文件的 awk 脚本?

如何加速使用多个大 gzip 文件的 awk 脚本?

我有两个数据文件:

  1. 文件_1.in,包含超过 2k 行,例如“12 AB0001”

    10 AB0001
    11 AC0002
    12 AD0003
    ...
    
  2. 我应该提取并解析gzip压缩文件的列表*.gz(大约1到3百万行)以创建一个名为lines(第二列)的输出文件文件_1.in

    ##comment..
    ##comment..
    #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT AB0001 AC0002 AD0003
    21 1234567 ab11111 G A 100 PASS info1;info2 GT 0|0 0|1 0|0
    21 1234568 ab22222 C A 100 PASS info1,info2 GT 1:23:2 .:.:. 0:32:2
    21 1234569 ab33333 A C 100 PASS info1;info2 GT 0|2 1|0 0|0
    

尝试不同的方法我得出这样的结论:

{
if(FNR==NR){list[$1]=$2;next}
if(!/^#/){
    for(p in list){
        split($p, sp, ":");
        if (sp[1] != "0|0" && sp[1] != "0" && sp[1] != "."){
            printf("%s %s %s %s %s %s %s %s %s %s\n", 
            $1, $2, $3, $4, $5, $6, $7, $8, $9, $p) >> out"/"list[p]".tmp"
        }
    }
} 
}

通过命令行执行:

awk -v out="outfolder/" -f myscript.awk file_1.in <(zcat *.gz)

但仅创建一个文件就需要两个多小时。有办法改进我的代码吗?我认为大部分时间都花在了zcat每个文件和似乎很慢的追加写入功能上。你怎么认为?

答案1

此代码在循环中执行大量处理,这些处理在迭代之间是相同的。 awks 没有复杂的循环优化来能够将其提升到循环之外:

    for(p in list){
        split($p, sp, ":");
        if (sp[1] != "0|0" && sp[1] != "0" && sp[1] != "."){

在读取第一个文件来填充关联数组后,关联list数组不会更改,但您会重复拆分索引值来测试此条件。相反,您可以遍历数组,然后删除不匹配的条目。或者...在处理时首先不要将这些条目插入数组file_1.in!然后可以删除拆分和测试:循环无条件访问 中的每个条目list

            printf("%s %s %s %s %s %s %s %s %s %s\n", 
            $1, $2, $3, $4, $5, $6, $7, $8, $9, $p) >> out"/"list[p]".tmp"

此处打印的材料对于循环的每次迭代都是相同的,除了最后一个%s采用 的字段之外$p。您可以使用sprintf循环外部将九个字段格式化为字符串str,然后执行以下操作: printf("%s %s", str, $p) >> out "/" list[p] ".tmp"

尚不清楚的是:假设$p中的表达式是一个适合索引字段的整数。所以整个逻辑看起来是假的:它在角色上进行分割,然后测试该分割的某些字段。如果预期为正整数,则不应包含任何冒号。您可能打算在这里测试第二列,即?printfpsplitp:plist[p]

除此之外,该程序可能会明显变慢。它维护着数千个打开的文件描述符,通过这些描述符将少量的材料添加到众多的输出文件中。数百万行来自压缩文件:对于每一行,循环都会迭代包含超过两千个条目的 assoc 数组,并将输出生成到许多不同的文件中。因此,数百万条压缩行变成了数十亿条未压缩行。

您可能想问自己将数据展开为该表示的目的是什么?如果是为了一些后续处理,也许可以使用更节省空间的表示方式。该awk代码看起来像是在大量预先计算某种关联,某种数据库可能能够隐式处理该关联。

相关内容