AWK/GAWK 性能

Mik*_*uke 6 performance awk gawk

我有一个 8400 万行的 XML,正在 Red Hat Linux 中使用“gawk”进行处理。(好吧,有些人会建议使用其他工具而不是 GAWK,但我的 XML 没有多行标记或任何其他特性,使 GAWK 不是这项工作的良好选择。)

我关心的是性能。

我最初的 AWK 脚本是这样的:

# Test_1.awk
BEGIN {FS = "<|:|=";}
{
if ($3 == "SubNetwork id")
    {
    # do something
    }
}
END {
# print something
}
Run Code Online (Sandbox Code Playgroud)

每行进行一次 8400 万次字符串比较。

我注意到“SubNetwork id”仅在该行中有 4 个字段 (NF=4) 时出现,因此我更改了脚本以减少字符串比较:

# Test_2.awk
BEGIN {FS = "<|:|=";}
{
if (NF == 4)
    {
    if ($3 == "SubNetwork id")
        {
        # do something
        }
    }
}
END {
# print something
}
Run Code Online (Sandbox Code Playgroud)

我运行它,发现我检查了 '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>
Run Code Online (Sandbox Code Playgroud)

任何有助于理解此性能问题的帮助将不胜感激。

提前致谢。

use*_*264 1

下面是一个简单的测试。第一行将 10,000,000 行“abc 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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,检查 $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
Run Code Online (Sandbox Code Playgroud)

因为/Hahaha/不需要任何解析。

如果您/SubNetwork id/在 之前添加{,可能会加快速度。

如果您只处理带有“SuNetwork id”的行并忽略所有其他行,您可能需要这样做

grep 'SubNetwork id' your_input_file | awk -f prog.awk
Run Code Online (Sandbox Code Playgroud)

它会大大加快速度,因为 grep 比 awk 快得多。

最后,另一种加速 awk 的方法是使用 mawk,它比 gawk 快得多。不幸的是,有时它会产生与 gawk 不同的结果,因此应该始终对其进行测试。