此AWK的性能问题-此代码在5GB输入文件上需要大约200分钟



下面的代码在5GB文件上运行,它消耗了99%的CPU,我想知道我是否做错了什么,或者有什么可以改善执行时间。

2013-04-03 08:54:19989 INFO〔Logger〕2013-04-03T08:54:19.987-04:00PCM.common.manage.springUtil<日志消息正文>lt;页眉>amp;lt;fedDKPLoggingContext id="DKP_DumpDocumentProperties"type="context.generated.FedDKPLogingContext"&gt&lt;日志过滤器&gt;7&lt/日志过滤器&gt&lt;logSeverity&gt;255&lt/logSeverity&gt&lt;schemaType&gt;PCMC.MRP.DocumentMetaData&lt/schemaType&gt&lt;UID&gt;073104c-4e-4ce-bda-69434ee62&lt/UID&gt&lt;consumerSystemId&gt;JTR&lt/consumerSystemId&gt&lt;consumerLogin&gt;jbserviceid&lt/consumerLogin&gt&lt;日志位置&gt;成功完成服务&lt/日志位置&gt&lt/fedDKPLoggingContext&gt<页眉>lt;有效载荷>0<有效载荷>lt/日志消息正文>

这是我正在使用的代码。我也尝试过gz格式,但都是徒劳的。我在下面的命令中称这个来自bash的awk。

awk-f mytest.awk<(gzip-dc扫描文件。$yeth.gz)|gzip>tem.gz

cat mytest.awk
#!/bin/awk -f
function to_ms (time, time_ms, s) {
    split(time, s, /:|,/ )
    time_ms = (s[1]*3600+s[2]*60+s[3])*1000+s[4]
    #printf ("%sn", newtime)
    return time_ms
}
{
   stid = gensub(/.*UID&amp;gt;([^&]+).*/,"\1","")
}
(stid in starttime) {
    etime = to_ms($2)
    endtime[stid] = etime
    docid[stid] = gensub(/.*id="([^""]+).*/,"\1","")
    consumer[stid]= gensub(/.*schemaType&amp;gt;PNC.([^.]+).*/,"\1","")
    state[stid]= gensub(/.*lt;logLocation&amp;gt;([^'' ]+).*/,"\1","")
    next
}
{
    stime = to_ms($2)
    starttime[stid] = stime
    st_hour[stid] = stime/(60*60*1000)
    timestamp[stid] = $1" "$2
}
END {
    print "Document,Consumer,Hour,ResponseTime,Timestamp,State"
    for (x in starttime) {
        for (y in endtime) {
            if (x==y) {
                diff = (endtime[y]-starttime[x])
                st = sprintf("%02d", st_hour[x])
                print docid[y], consumer[y], st":00", diff, timestamp[x], state[y] |"sort -k3"
                delete starttime[x]
                delete endtime[y]
                delete docid[y]
                delete consumer[y]
                delete timestamp[x]
                delete state[y]
            } 
        }
    }
}

假设每个stid只有一个结束时间-不要建立一个开始时间和结束时间的数组,然后在它们之间循环,只要在达到结束时间时处理stid即可。即不是像今天这样:

{ stid = whatever }
stid in starttime {
   populate endtime[stid]
   next
}
{ populate starttime[stid] }
END {
   for (x in starttime) {
      for (y in endtime) {
          if (x == y) {
              stid = x
              process endtime[stid] - starttime[stid]
          }
      }
   }
}

但是这个:

{ stid = whatever }
stid in starttime {
   process to_ms($2) - starttime[stid]
   delete starttime[stid]
   next
}
{ populate starttime[stid] }

如果你不能做到这一点,例如,由于有多个记录具有相同的stid,并且你想从第一个和最后一个记录中获得时间戳,那么将END部分中的循环更改为只循环通过你获得结束时间的stid(因为你已经知道它们有相应的开始时间),而不是试图在starttime和endtime上找到那些巨大循环中的所有stid,例如:

{ stid = whatever }
stid in starttime {
   populate endtime[stid]
   next
}
{ populate starttime[stid] }
END {
   for (stid in endtime) {
      process endtime[stid] - starttime[stid]
   }
}

无论采用哪种方法,您都应该看到性能的巨大提高。

END部分中,它总是经过内部for循环,即使找到了y项,然后从endtime数组中删除了它。我建议使用break跳出内部循环。

另一方面(正如我所见)根本不需要内部循环!它试图在关联数组中查找具有已知关键字的元素。

此外,我可能会建议不要删除找到的项目。在关联数组中查找项目是在恒定的时间内完成的(取决于哈希键生成的算法和生成的重复项目数量),因此从这样的数组中删除项目不一定会加快进程,但删除项目肯定会减慢进程。

所以我可以建议使用这个:

for (x in starttime) {
    if (x in endtime) {
        diff = (endtime[x]-starttime[x])
        st = sprintf("%02d", st_hour[x])
        print docid[x], consumer[x], st":00", diff, timestamp[x], state[x] |"sort -k3"
    }
}

使用gzip甚至会消耗更多的CPU资源,但您可以腾出一些I/O带宽。

@Ed,第一种方法并没有给我带来预期的结果。就是这样做的

# end time and diff 
(stid in starttime) 
{ etime = to_ms($2) 
diff = etime - stime
 print diff,stid 
delete starttime[stid] 
next } 
# Populate starttime
 { 
stime = to_ms($2) 
starttime[stid] = stime 
st_hour[stid] = stime/(60*60*1000)
 }

o/p就像左这个sud进来了毫秒和stid。

561849 c858591f-e01b-4407-b9f9-48302b65c383562740 c858591f-e01b-4407-b9f9-48302b65c383563629 56c71ef3-d952-4261-9711-16b18a32c6ba564484 56c71ef3-d952-4261-9711-16b18a32c6ba

最新更新