【问题标题】:Perl / xargs terrible performance with xargs -n1/-iPerl / xargs xargs -n1/-i 的性能很差
【发布时间】:2018-06-02 13:17:27
【问题描述】:

我写了一点 perl 单行:

find . -name '*.cpp' -print0 2>/dev/null | xargs -0 -i perl -ne 'if (/\+\+\S*[cC]ursor\S*/ && !/[!=]=\s*DB_NULL_CURSOR/) {print "$ARGV:$.\n $_\n";}' {}

在我运行它的目录中,find 部分返回 5802 个结果。

现在,我知道xargs -i(或-n1)会对性能产生影响,但-i

find . -name '*.cpp' -print0 2> /dev/null  0.33s user 1.12s system 0% cpu 3:12.57 total
xargs -0 -i perl -ne  {}  4.12s user 32.80s system 16% cpu 3:42.22 total

没有:

find . -name '*.cpp' -print0 2> /dev/null  0.27s user 1.22s system 95% cpu 1.556 total
xargs -0 perl -ne   0.62s user 0.69s system 61% cpu 2.117 total

几分钟与几秒钟(确认测试顺序无关紧要)。除了在第二个实例中明显不正确的行号之外,实际的 perl 结果是相同的。

Cygwin/bash/perl5v26 和 WSL Ubuntu 16.04/zsh/perl5v22 中的行为相同。在这两种情况下,文件系统都是 NTFS。但是...我有点假设我写的那个小单行字一定有某种错误,而那些东西是无关紧要的?

编辑:我突然想到在启动时使用-f 禁用sitecustomize.pl --一个我隐约记得使用perl --help 看到的选项可能会有所帮助。它没。另外,我知道由于 perl 编译正则表达式,-i 的性能影响将是显着。这似乎仍然失控。

【问题讨论】:

  • 您不需要 xargs 或并行,您只需要直接通过管道传输到 perl,如果它们符合您的正则表达式规则,它将遍历您的文件名并打印它们。
  • 甚至find ... -exec perl -ne '...' {} + 也可以避开管道。
  • @xxfelixxx 没有 xargs,您必须在 perl 中打开文件,并且由于 -print0,您不会逐行获取它们。也许最好简单地使用带有File::Find的perl脚本@
  • 所以它需要 55 倍的时间,但你产生的数量会增加 100 倍到 8000 倍 perl 解释器...
  • @tripleee,虽然您可以通过使用-exec 而不是-print0 来避免xargs,但-exec ; 的性能应该类似于xargs -n 1(并且-exec + 的性能应该类似于@987654340 @ 没有-n 1)。

标签: perl xargs


【解决方案1】:

xargs 将为它处理的每一行调用一个新进程,因此在您的情况下,它将旋转 perl 5802 次并连续执行此操作

你可以试试parallel

您可能正在使用 xargs 来调用计算密集型命令 每一行输入。如果 xargs 允许你带走不是很好吗 您机器中的多核优势?这就是-P 为了。它允许 xargs 多次调用指定的命令 平行线。例如,您可以使用它来运行多个 ffmpeg 并行编码。但是我只是要给你看另一个 人为的例子。

或者另一方面,您可以使用sed,这样启动起来要轻得多

【讨论】:

  • 是的,我以前用过-P8,它加快了速度。 sed 除了简单的单个正则表达式命令之外,我并不十分熟悉。我认为在 perl 中手动执行行读取循环或使用 awk 都是不错的选择
【解决方案2】:

好吧,我的基本误解是假设最大命令行长度在 2000 范围内。所以我假设每 20 个文件左右都有一个 perl 实例(每个大约 120 个字符)。这是非常错误的。

getconf ARG_MAX 显示实际可接受的长度。就我而言:

2097152

所以,我正在查看 1 个 perl 实例与 5802 个实例。我能想到的唯一 perl 解决方案是删除 -n 并手动实现循环,显式关闭每个文件。

我认为更好的解决方案是 awk:

 find . -name '*.cpp' 2>/dev/null -print0 | xargs -0 awk '{if (/\+\+\S*[cC]ursor\S*/ && !/[!=]=\s*DB_NULL_CURSOR/) {print FILENAME ":" FNR "  " $0}}'

或grep:

find . -name '*.cpp' 2>/dev/null -print0 | xargs -0 grep -nE '\+\+\S*[cC]ursor\S*' | grep -v '[!=]=\s*DB_NULL_CURSOR'

两者都在 2 或 3 秒范围内执行。

【讨论】:

    猜你喜欢
    • 2011-07-08
    • 1970-01-01
    • 2015-10-16
    • 1970-01-01
    • 2014-06-15
    • 1970-01-01
    • 2019-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多