【问题标题】:AWK performance while processing big files处理大文件时的 AWK 性能
【发布时间】:2014-10-01 19:43:14
【问题描述】:

我有一个 awk 脚本,用于计算完成某些事务所需的时间。该脚本获取每个事务的唯一 ID,并存储每个事务的最小和最大时间戳。然后计算差异,最后显示超过 60 秒的结果。

与几千 (200k) 一起使用时效果很好,但在现实世界中使用时需要更多时间。我测试了几次,处理大约2800万行大约需要15分钟。我可以考虑这个良好的性能还是可以改进它?

我愿意接受任何建议。

这里有完整的代码

zgrep -E "\(([a-z0-9]){15,}:" /path/to/very/big/log |  awk '{
gsub("[()]|:.*","",$4); #just removing ugly chars
++cont
min=$4"min" #name for maximun value of current transaction
max=$4"max" #same as previous, just for readability 
split($2,secs,/[:,]/) #split hours,minutes and seconds
seconds = 3600*secs[1] + 60*secs[2] + secs[3] #turn everything into seconds
if(arr[min] > seconds || arr[min] == 0)
  arr[min]=seconds
if(arr[max] < seconds)
   arr[max]=seconds
dif=arr[max] - arr[min]
if(dif > 60)
  result[$4] = dif
}
END{
for(x in result)
   print x" - "result[x]
print ":Processed "cont" lines"
}'

【问题讨论】:

  • 关于编辑:部分 print "new MO found! "name " start at "seconds (我为 min[name] 赋值的块) 仅执行 ~=800 次,但最后,它报告min的长度约为80k,这是非价值索引的数量

标签: bash awk


【解决方案1】:

您无需在每次读取记录时计算差异。只需在 END 部分执行一次即可。

您不需要那个 cont 变量,只需使用 NR。

您不需要分别填充 min 和 max 字符串连接在 awk 中很慢。

您不应更改 $4,因为这将强制重新编译记录。

试试这个:

awk '{
    name = $4
    gsub(/[()]|:.*/,"",name); #just removing ugly chars

    split($2,secs,/[:,]/) #split hours,minutes and seconds
    seconds = 3600*secs[1] + 60*secs[2] + secs[3] #turn everything into seconds

    if (NR==1) {
        min[name] = max[name] = seconds
    }
    else {
        if (min[name] > seconds) {
            min[name] = seconds
        }
        if (max[name] < seconds) {
            max[name] = seconds
        }
    }
}

END {
    for (name in min) {
        diff = max[name] - min[name]
        if (diff > 60) {
            print name, "-", diff
        }
    }
    print ":Processed", NR, "lines"
}'

【讨论】:

  • 这很有意义。明天我会检查并告诉你。非常感谢
  • 您好 Ed,在尝试了您的解决方案后,我有几个 cmets:NR==1 在我的场景中没有意义,因为任何行都可以是事务的第一行。擦除该行并使用 min[name]==0 作为启动器后,我进行了一些测试,得到了类似的结果:# your proposal: real 13m45.390s user 18m9.014s# # my code: real 13m0.101s user 17m47.016s # 另外,您能否解释一下为什么修改 $4 会使 awk 重新编译整行?只是因为它是我想
  • 测试min[name]=="",而不是==0。修改字段重新编译记录,将 FS 替换为 OFS,这只是 awk 的功能之一。进行计时时 - 确保您运行每个示例 3 次并获取每个示例第三次运行的结果,因此缓存不是一个因素。我的运行速度应该比你的快得多,因为我只为每个最终的最小/最大对计算一次差异,而不是为每一行计算一次,也没有重新编译记录并删除cont 的增量。您是否在运行中包含zgrep?有没有可能这就是一直在用的东西?
  • 感谢您的大力帮助。我同意您的实现应该更快,因为存在数百万次操作的差异。您的意思是我应该连续运行每个测试树时间还是只运行树时间?是的,我在所有运行中都包含 zgrep。我开始认为这是瓶颈,因为所有脚本都需要相似的时间来运行。我需要 zgrep 仅选择与事务相对应的行。如果我在 awk 中进行此检查会更快吗?我不明白为什么它这么慢。当我使用带有单个字符串的 zgrep 时只需几秒钟,它也会读取所有行。
  • 我的意思是运行一个脚本 3 次并为其获取第三次输出,然后对另一个脚本执行相同的操作。关于 zgrep 的 Idk 但您可以将其输出保存在一个文件中,然后在该文件上运行 awk 以分隔持续时间。
【解决方案2】:

在做了一些测试后,根据 Ed Morton 的建议(代码改进和性能测试),我发现瓶颈是 zgrep 命令。这是一个做几件事的例子:

  • 检查我们是否有交易行(首先如果)
  • 清除事务id
  • 通过检查它是否在数组中来检查它是否已经注册(第二个 if)
  • 如果未注册,则检查它是否是适当的交易类型,如果是,则以秒为单位注册时间戳
  • 如果已注册则将新的时间戳保存为最大值
  • 毕竟它进行了计算时间差的必要操作

非常感谢所有帮助过我的人。

zcat /veryBigLog.gz |  awk '
{if($4 ~ /^\([:alnum:]/ ){
    name=$4;gsub(/[()]|:.*/,"",name);
    if(!(name in min)){
        if($0 ~ /TypeOFTransaction/ ){
          split($2,secs,/[:,]/) 
          seconds = 3600*secs[1] + 60*secs[2] + secs[3] 
          max[name] = min[name]=seconds
          print lengt(min) "new "name " start at "seconds
       }
       }else{ 
            split($2,secs,/[:,]/)
            seconds = 3600*secs[1] + 60*secs[2] + secs[3]
            if( max[name] < seconds) max[name]=seconds
            print name " new max " max[name]
        }
        }}END{
            for(x in min){
                dif=max[x]- min[x]
                print max[x]" max - min "min[x]" : "dif
            }
            print "Processed "NR" Records"
            print "Found "length(min)" MOs" }' 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-30
    • 2012-08-17
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 2012-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多