如何优化这个 Unix 命令?

如何优化这个 Unix 命令?

以下命令大约需要10分钟才能输出结果

find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
    awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head

我怎样才能提高它的性能?

答案1

这已经是相当优化了。如果不了解更多细节,就很难知道瓶颈是什么:

  • 存储类型(HD、SSD、网络、RAID)
  • 匹配文件的数量和平均大小
  • 目录和其他不匹配文件的数量
  • 每行的字段数
  • 线的平均长度

在任何情况下你都可以做的事情:

  • 如果您的/支持,请替换-print | xargs-exec cmd {} +或。不仅是错误的,而且还更昂贵,因为需要解码字符以找出哪些字符是空白并进行一些昂贵的引用处理。-print0 | xargs -r0findxargs-print | xargsxargs
  • 将语言环境固定为 C ( export LC_ALL=C)。由于此处涉及的所有字符(|以及文件内容的十进制数字和文件名的拉丁字母、句点和下划线)都是可移植字符集的一部分,因此如果您的字符集是 UTF-8 或其他一些多字节字符集,请切换具有单字节字符集的 C 语言将为find和节省大量工作awk
  • 将部分简化awk为:awk -F "|" '$14 == "20160920100643" && $22 == "567094398953"'.
  • 由于您要将输出通过管道传输到head,您可能需要禁用 的输出缓冲,awk以便它尽早输出这 10 行。通过gawkmawk,您可以使用fflush()它。或者你可以添加一个if (++n == 10) exitin awk.

总结:

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -exec zcat {} + |
  awk -F "|" '$14 == "20160920100643" && $22 == "567094398953" {
    print; if (++n == 10) exit}')

如果 CPU 是瓶颈,在多核 GNU 系统上,您可以尝试:

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -print0 |
  xargs -r0P 4 -n 100 sh -c '
    zcat "$@" | 
      awk -F "|" "\$14 == "20160920100643" && \$22 == "567094398953" {
        print; fflush()}"' sh | head)

zcat | awk对 100 个文件批次并行运行 4 个作业。

如果这20160920100643是一个时间戳,您可能希望排除在此之前最后修改的文件。对于 GNU 或 BSD find,添加-newermt '2016-09-20 10:06:42'.

如果行有大量字段,您会因awk拆分行并分配如此多的$n字段而受到惩罚。使用仅考虑前 22 个字段的方法可以加快速度:

grep -E '^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)'

而不是awk命令。使用 GNU grep,添加--line-buffered选项以在并行方法中尽早输出行,或者-m 10在非并行方法中在 10 个匹配后停止。

总而言之,如果 CPU 是瓶颈,并且您的系统上至少有 4 个 CPU 核心,并且至少有 400 个 muc* 文件,并且您使用的是 GNU 系统(通常grep比 GNU 快得多awk):

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -newermt '2016-09-20 10:06:42' -print0 |
  xargs -r0P 4 -n 100 sh -c '
    zcat "$@" | 
      grep --line-buffered -E \
        "^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)"
  ' sh | head)

请注意,在并行方法中,您可能会得到命令相互混合的输出grep(尽管使用行缓冲并且提供的行小于几千字节大,但应保留行边界)。

答案2

@Stéphane Chazelas 的回答提供了有关如何优化命令管道的大量详细信息

find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
    awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head

我将提供另一种方法来解决这个问题,您可以实际测量自己在哪里花费最多的时间。一旦找到时间花在哪里,您就可以决定如何处理它。如果你想提高 10 分钟的运行时间,优化一个需要 2 秒的步骤几乎是没有用的。

当我查看命令管道时,三件事引起了我的注意:

  1. find .- 目录结构是什么样的?每个目录有多少个文件?该目录是运行命令的系统的本地目录吗?远程文件系统将是很多慢点。
  2. -name "muc*_*_20160920_*.unl*"- 目录结构中所有文件名的接近程度如何?它们是否都与名称“接近”并且难以/CPU 密集型匹配?因为每一个目录树中的文件必须从磁盘读取其名称并与模式进行比较。
  3. xargs zcat-xargs在我看来,这并不是一个太大的性能问题,特别是与find上述问题及其zcat本身相比。即使是 10,000 个甚至 10,000,000 个文件名,与所花费的时间相比,仅传递和解析名称所花费的时间几乎可以忽略不计发现名称,然后打开并解压缩所有文件本身。文件有多大?因为你正在解压缩整个每一个与您的文件名模式匹配的文件find

您如何确定主要的性能问题是什么?测量管道中每个命令的性能。 (看https://stackoverflow.com/questions/13294554/how-to-use-gnu-time-with-pipeline有关对整个管道进行计时的详细信息。)您可以运行以下命令并查看每个步骤占整个管道的处理时间的时间:

/usr/bin/time find .- 这告诉您运行目录树需要多长时间。如果速度很慢,则需要更好的存储系统。 刷新你的文件系统缓存在对其进行计时以获得最坏情况测量之前,然后再次运行计时find并查看缓存对性能的影响有多大。如果该目录不是本地目录,请尝试在文件所在的实际系统上运行该命令。

/usr/bin/time find . -name "muc*_*_20160920_*.unl*"- 这将告诉您模式匹配文件名需要多长时间。再次刷新文件系统缓存并运行两次。

/usr/bin/time bash -c "find . -name 'muc*_*_20160920_*.unl*' | xargs zcat > /dev/null"- 我怀疑这是您的管道长时间运行时间的主要组成部分。如果这是问题所在,那么并行化zcat每个 Stéphane Chazelas 答案的命令可能是最好的答案。

继续将原始命令管道中的步骤添加到正在测试的管道中,直到找到您花费最多时间的地方。我再次怀疑这是zcat步骤。如果是这样,zcat@Stéphane Chazelas 发布的并行化也许会有所帮助。

并行化zcat可能没有帮助——甚至可能伤害性能并减慢处理速度。由于zcat一次只运行一个,IO 可能会处于一种良好的流模式,从而最大限度地减少磁盘寻道。当多个zcat进程同时运行时,IO 操作可能会发生竞争,并且实际上会减慢处理速度,因为磁盘头需要寻道,并且任何预读操作的效率都会降低。

如果该zcat步骤是您的主要性能瓶颈,并且zcat一次运行多个进程无济于事或实际上会减慢您的速度,则您的管道受 IO 限制,并且您需要通过使用更快的存储来解决该问题。

再说一次 - 如果目录不是运行命令管道的计算机的本地目录,请尝试在文件系统实际所在的计算机上运行它。

答案3

正如评论中指出的兹格列普是此类任务的更好选择环球星允许用作的**选项all path inside the directory except hidden

shopt -s globstar
zgrep -m 10 '^\([^|]*|\)\{13\}20160920100643|\([^|]*|\)\{7\}567094398953' ./**muc*_*_20160920_*.unl*
shopt -u globstar

答案4

正如所指出的,如果没有一些额外的细节,就不可能给出正确的答案。

locate -0 -b -r '^muc.*_.*_20160920_.*.unl.*gz' | 
   xargs -0  zcat |
   awk -F "|" '$14=="20160920100643" && $22=="567094398953"'| head
  • **1:locate(如果可用)比或快得多find;使用的正则表达式必须调整...

  • 2和3:PO的过滤器

正如 @rudmeier 明智地指出的那样,存在有关 . 的可用性和更新状态的问题locate。 (例如,在大多数 Linux 机器中,locate 每天都会更新;这样它将无法找到今天创建的文件)

然而,如果locate可用,这将产生非常令人印象深刻的加速。

如果采购订单能够提供time ...各种解决方案,那就很有趣了

相关内容