【问题标题】:How to speed up grep/awk command?如何加速 grep/awk 命令?
【发布时间】:2019-05-28 02:22:36
【问题描述】:

我将处理文本文件 (>300 GB) 并将其拆分为小文本文件 (~1 GB)。我想加快 grep/awk 命令的速度。

我需要对 b 列有值的行进行 grep,这是我的方法:

# method 1:
awk -F',' '$2 ~ /a/ { print }' input

# method 2:
grep -e ".a" < inpuy

两种方式每个文件都需要 1 分钟。那么我怎样才能加快这个操作呢?


输入文件示例:

a,b,c,d
1,4a337485,2,54
4,2a4645647,4,56
6,5a3489556,3,22
9,,3,12
10,0,34,45
24,4a83944,3,22
45,,435,34

预期的输出文件:

a,b,c,d
1,4a337485,2,54
4,2a4645647,4,56
6,5a3489556,3,22
24,4a83944,3,22

【问题讨论】:

  • 其他列可以为空吗?如果不是,fgrep -v ',,' input 的性能应该比grep -e 稍好。
  • 愚蠢的问题......你为什么使用外壳?如果由于大数据而需要考虑性能,为什么不编写优化的 C 程序呢? (我假设你会不止一次这样做)。
  • 数据从何而来?你怎么得到它?这些巨大的文件包含哪些实际数据?你是如何运行你的基准测试的?请edit你的问题来改进它(我投票结束,因为太宽泛和不清楚)
  • @infaak,恕我直言,首先你应该有一个日志轮换机制,这样你的盒子里就没有大尺寸的文件(除非它是一个数据文件并且你有一个用户数据它,我对此表示怀疑),它真的会为您节省任何其他空间问题和不必要的问题。
  • 您提到要处理一个 300GB 的文件并将其拆分为较小的文件。您提出的问题似乎是一个更大的计划的一部分,该计划将适应分裂。如果这是真的,我相信使用单个 awk,我们可以通过一次读取将 300GB 拆分为更小的文件。但是,我们需要了解这样做的条件。

标签: linux performance awk grep


【解决方案1】:

如何加速 grep/awk 命令?

您确定grepawk 是您感知缓慢的罪魁祸首吗?你知道cut(1)sed(1) 吗?您是否对在您的数据上运行 wc(1) 的时间进行了基准测试?可能文本 I/O 会花费很多时间。

多次进行基准测试,并使用time(1) 对您的程序进行基准测试。

我有一个高端 Debian 桌面(配备 AMD 2970WX、64Gb RAM、1Tbyte SSD 系统磁盘、多 TB 7200RPM SATA 数据磁盘),并且仅在 25Gbyte 文件上运行 wc(一些 *.tar.xz 存档)坐在硬盘上需要超过 10 分钟(用 time 测量),wc 正在通过读取该文件进行一些非常简单的文本处理顺序所以应该在 same 数据上运行速度比 grep 快(但令我惊讶的是,没有!)或 awk

wc /big/basile/backup.tar.xz  640.14s user 4.58s system 99% cpu 10:49.92 total

和(在 same 文件上使用grep 来计算a 的出现次数)

grep -c a /big/basile/backup.tar.xz  38.30s user 7.60s system 33% cpu 2:17.06 total

对您问题的一般回答:

只需编写巧妙(高效O(log n) time complexity data structures: red-black trees, 或 hash tables , etc ...) C 或 C++ 或 Ocaml 或大多数其他优秀语言和实现的等效程序。或者购买更多内存来增加您的page cache。或者购买SSD 来保存您的数据。并且多次重复您的基准测试(因为页面缓存)。

针对您的问题的建议:使用关系数据库

使用 300Gb 的纯文本文件可能不是最好的方法。 拥有巨大的文本文件通常是错误的,并且在您需要处理多次相同的数据时很可能是错误的。你会更好pre-process它以某种方式..

如果您在 same 数据文件上多次重复 same grep 搜索或 awk 执行,考虑改用 sqlite(参见this 答案)甚至一些other 真实relational database(例如使用PostGreSQL 或其他一些好的RDBMS)来存储然后处理您的原始数据。

因此,一种可能的方法(如果您有足够的磁盘空间)可能是编写一些程序(用 C、Python、Ocaml 等...),由您的原始数据提供,并填充一些 sqlite 数据库。一定要有聪明的database indexes,并花时间设计足够好的database schema,注意database normalization

【讨论】:

  • 这已经过了你的有效点,但wc 时间看起来太可怕了,我只好自己去看看。我有一个 9 GB 的 json 文件,time wc 生成了 real 2m51.558s,这与您的观察一致。然而,time mawk '{nf+=NF;l+=length()+1}END{printf "%d %d %.0f", NR,nf,l}' 产生了 real 1m0.348s,这有点令人惊讶,因为我原以为 wc 是一个超越 mawk 的专用工具。
【解决方案2】:

使用 mawk,避免正则表达式并执行:

$ mawk -F, '$2!=""' file
a,b,c,d
1,4a337485,2,54
4,2a4645647,4,56
6,5a3489556,3,22
10,0,34,45
24,4a83944,3,22

让我们知道这花了多长时间。

根据结果,我对您的 10M 条数据记录进行了一些测试:使用 mawk 和正则表达式:

GNU awk 和正则表达式:

$ time gawk -F, '$2~/a/' file > /dev/null

real    0m7.494s
user    0m7.440s
sys     0m0.052s

GNU awk 并且没有正则表达式:

$ time gawk -F, '$2!=""' file >/dev/null

real    0m9.330s
user    0m9.276s
sys     0m0.052s

mawk 并且没有正则表达式:

$ time mawk -F, '$2!=""' file >/dev/null

real    0m4.961s
user    0m4.904s
sys     0m0.060s

mawk 和正则表达式:

$ time mawk -F, '$2~/a/' file > /dev/null

real    0m3.672s
user    0m3.600s
sys     0m0.068s

【讨论】:

  • 哦,您对 has values on column b 的定义与我的不同(不过请注意您的标题行)。 mawk -F, '$2!="" &amp;&amp; $2!=0' file 将为此输入修复它,但如果您只想要 b 字段中包含 a 的记录,请使用:mawk -F, '$2~/a/ file`。
【解决方案3】:

我怀疑您真正的问题是您重复调用 awk(可能在循环中),每组 $2 值一次并每次生成一个输出文件,例如:

awk -F, '$2==""' input > novals
awk -F, '$2!=""' input > yesvals
etc.

不要这样做,因为它会在每次迭代时读取整个文件,效率非常低。改为这样做:

awk '{out=($2=="" ? "novals" : "yesvals")} {print > out}' input

这将通过一次调用 awk 创建所有输出文件。一旦超过大约 15 个输出文件,就需要 GNU awk 来对打开的文件描述符进行内部处理,或者您需要在 $2 更改时添加 close(out)s 并使用 &gt;&gt; 而不是 &gt;

awk '$2!=prev{close(out); out=($2=="" ? "novals" : "yesvals"); prev=$2} {print >> out}' input

如果您首先对输入文件进行排序,那么效率会更高(如果您关心保留唯一 $2 值的输入排序,则需要 GNU 排序以实现稳定排序 -s):

sort -t, -k2,2 -s

【讨论】:

    猜你喜欢
    • 2019-10-07
    • 1970-01-01
    • 2015-11-05
    • 1970-01-01
    • 2015-01-30
    • 1970-01-01
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多