我有一个8400万行的XML,我在Red Hat Linux中用“gawk”处理它。(好吧,有些人会建议使用其他工具,而不是GAWK,但是我的XML没有多行标记或任何其他使GAWK不适合这项工作的特性。)
我关心的是性能。
我的初始AWK脚本是这样的:
# Test_1.awk
BEGIN {FS = "<|:|=";}
{
if ($3 == "SubNetwork id")
{
# do something
}
}
END {
# print something
}这就进行了8400万次字符串比较,每行一次。
我注意到只有在行(NF=4)中有4个字段时才会出现"SubNetwork id“,所以我更改了脚本以进行更少的字符串比较:
# Test_2.awk
BEGIN {FS = "<|:|=";}
{
if (NF == 4)
{
if ($3 == "SubNetwork id")
{
# do something
}
}
}
END {
# print something
}我运行它,发现我检查了'NF == 4‘8400万次(很明显)和'$3 == "SubNetwork id"’只有300万次。很好,我减少了字符串比较的次数,我一直认为这比简单的整数比较更耗时(NF是整数,对吧?)。
当我测试这两个脚本的性能时,我大吃一惊。在大多数情况下,Test_1比Test_2快,我会多次运行它们来计算其他可能正在使用CPU时间的进程,但总的来说,我的测试都是在CPU或多或少处于“空闲”状态时运行的。
我的大脑告诉我,8400万个整数比较加上300万个字符串比较肯定比8400万个字符串比较快,但很明显,我的推理出了问题。
我的XML如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<ConfigDataFile xmlns:un="specific.xsd" xmlns:xn="generic.xsd">
<configData dnPrefix="Undefined">
<xn:SubNetwork id="ROOT_1">
<xn:SubNetwork id="ROOT_2">
<xn:attributes>
...
</xn:attributes>
</xn:SubNetwork>
<xn:SubNetwork id="ID_1">
....
</xn:SubNetwork>
<xn:SubNetwork id="ID_2">
.....
</xn:SubNetwork>
</xn:SubNetwork>
</configData>
</ConfigDataFile>如果能帮助我们理解这个性能问题,我们将不胜感激。
提前谢谢。
发布于 2017-04-20 23:28:53
我做了更多的测试:
1-生成包含某些数据的大文件
yes 'SomeSampleText SomeOtherText 33 1970 YetAnotherText 777 abc 1 AndSomeMore' | head -12000000 > SomeData.txt分隔符是空格!
2-多次运行这6个测试,并计算每个测试的平均时间。我在3台不同的机器( Red Hat Linux Enterprise 4)上做到了这一点。
time gawk 'BEGIN {a = 0;} {if ($5 == "YetAnotherText") a ++;} END {print "a: " a;}' SomeData.txt
time gawk 'BEGIN {a = 0;} {if ($0 ~ /YetAnotherText/) a ++;} END {print "a: " a;}' SomeData.txt
time gawk 'BEGIN {a = 0;} /YetAnotherText/ {a ++;} END {print "a: " a;}' SomeData.txt
time gawk 'BEGIN {a = 0;} {if (NF == 9) a ++;} END {print "a: " a;}' SomeData.txt
time gawk 'BEGIN {a = 0;} {if ($1 == "SomeSampleText") a ++;} END {print "a: " a;}' SomeData.txt
time gawk 'BEGIN {a = 0;} {if ($9 == "AndSomeMore") a ++;} END {print "a: " a;}' SomeData.txt3-我得到了这些结果(数字是秒)
-- Machine 1
10.35
39.39
38.87
10.40
7.72
12.26
-- Machine 2
8.50
32.43
31.83
9.10
6.54
9.91
-- Machine 3
12.35
13.55
12.90
14.40
9.43
14.93在测试2和3中搜索pattern /YetAnotherText/看起来非常慢。除了机器3。
4-生成另一个具有不同分隔符的数据的大文件
yes "<SomeSampleText:SomeOtherText=33>1970<YetAnotherText:777=abc>1<AndSomeMore>" | head -12000000 > SomeData2.txt5-运行6个测试,更改FS
time gawk 'BEGIN {FS = "<|:|=";} {if ($5 == "YetAnotherText") a ++;} END {print "a: " a;}' SomeData2.txt
time gawk 'BEGIN {FS = "<|:|=";} {if ($0 ~ /YetAnotherText/) a ++;} END {print "a: " a;}' SomeData2.txt
time gawk 'BEGIN {FS = "<|:|=";} /YetAnotherText/ {a ++;} END {print "a: " a;}' SomeData2.txt
time gawk 'BEGIN {FS = "<|:|=";} {if (NF == 8) a ++;} END {print "a: " a;}' SomeData2.txt
time gawk 'BEGIN {FS = "<|:|=";} {if ($2 == "SomeSampleText") a ++;} END {print "a: " a;}' SomeData2.txt
time gawk 'BEGIN {FS = "<|:|=";} {if ($8 == "AndSomeMore>") a ++;} END {print "a: " a;}' SomeData2.txt6-我得到了这些结果(我只在机器3上做了,抱歉)
66.17
33.11
32.16
76.77
37.17
77.20我的结论(也可以查看@user31264的评论):
当有一个简单的分隔符时,解析和拆分到字段似乎更快,而不是几个分隔符。获取模式比获取模式更快,其中N
,则会更慢
发布于 2017-04-20 16:55:21
下面是一个简单的测试。第一行将10,000,000行"a b c d“输出到文件a。awk是GNU Awk 4.1.3
[~] yes 'a b c d' | h -10000000 > a
[~] time awk '{if(NF==5)print("a")}' a
2.344u 0.012s 0:02.36 99.5% 0+0k 0+0io 0pf+0w
[~] time awk '{if(NF==5)print("a")}' a
2.364u 0.008s 0:02.37 99.5% 0+0k 0+0io 0pf+0w
[~] time awk '{if($4=="Hahaha")print("a")}' a
2.876u 0.024s 0:02.90 99.6% 0+0k 0+0io 0pf+0w
[~] time awk '{if($4=="Hahaha")print("a")}' a
2.880u 0.020s 0:02.90 100.0% 0+0k 0+0io 0pf+0w
[~] time awk '{if($1=="Hahaha")print("a")}' a
2.540u 0.020s 0:02.56 100.0% 0+0k 0+0io 0pf+0w
[~] time awk '{if($1=="Hahaha")print("a")}' a
2.404u 0.004s 0:02.41 99.5% 0+0k 0+0io 0pf+0w正如您所看到的,检查$1比检查$4更快,因为在前一种情况下,AWK只需要解析直到第一个单词的行。如果你只检查NF,AWK只计算单词,这在我的例子中甚至更快,但在你的例子中,计算单词可能比解析输入行到第三个单词慢。
最后,我们可以像这样加速AWK:
[~] time awk '/Hahaha/{if($4=="Hahaha")print("a")}' a
1.376u 0.020s 0:01.40 99.2% 0+0k 0+0io 0pf+0w
[~] time awk '/Hahaha/{if($4=="Hahaha")print("a")}' a
1.372u 0.028s 0:01.40 99.2% 0+0k 0+0io 0pf+0w因为/Hahaha/不需要任何解析。
如果在{之前添加/SubNetwork id/,可能会加快速度。
如果只处理具有"SuNetwork id“的行,而忽略所有其他行,则可能需要这样做
grep 'SubNetwork id' your_input_file | awk -f prog.awk这将大大加快速度,因为grep比awk快得多。
最后,另一种提高awk速度的方法是使用mawk,它比gawk快得多。不幸的是,有时它会产生与gawk不同的结果,因此应该始终对其进行测试。
发布于 2017-04-20 17:52:04
另一个简单的测试
文件是整个样本的3.000.000行副本。结果是运行3次后的代表性时间(针对缓存和其他操作系统影响)
# time awk 'BEGIN{FS="[<:=]"}NF>=4{a++}END{print a+0}' YourFile
780100
real 0m1.89s user 0m1.74s sys 0m0.01s
# time awk 'BEGIN{FS="<|:|="}NF>=4{a++}END{print a+0}' YourFile
780100
real 0m2.00s user 0m1.91s sys 0m0.02s
# time awk 'BEGIN{FS="<|:|="}NF>=4&&/:SubNetwork/{a++}END{print a+0}' YourFile
780100
real 0m3.09s user 0m2.93s sys 0m0.02s
# time awk 'BEGIN{FS=":SubNetwork"}NF>=2{a++}END{print a+0}' YourFile
1560200
real 0m1.32s user 0m1.27s sys 0m0.02s
# time awk '/:SubNetwork/{a++}END{print a}' YourFile
1560200
real 0m3.23s user 0m3.06s sys 0m0.02s展示如果你使用:SubNetwork作为字段分隔符,它是最快的。
现在,对于下面的操作,您可能需要通过诸如FS="<|:|=";$1=$1"";$0=$0""; ... your action ...; FS=":SubNetwork"之类的东西拆分或重新分隔该字段
作为预过滤的额外测试
# time awk '$1 == "<xn:SubNetwork" || $1 == "<xn:Attributes" {a++}END{print a+0}' YourFile
780100
real 0m1.29s user 0m1.20s sys 0m0.03shttps://stackoverflow.com/questions/43513975
复制相似问题