Perl / xargs xargs 的糟糕表现 -n1/-i

Perl / xargs terrible performance with xargs -n1/-i

我写了一点 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部分returns5802结果。

现在,我知道 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 看到的一个选项——可能会有所帮助。它没有。另外,我知道 -i 的性能影响将是 显着 由于 perl 编译正则表达式。这似乎仍然无法控制。

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

你可以试试 parallel

You might be using xargs to invoke a compute intensive command for every line of input. Wouldn’t it be nice if xargs allowed you to take advantage of the multiple cores in your machine? That’s what -P is for. It allows xargs to invoke the specified command multiple times in parallel. You might use this for example to run multiple ffmpeg encodes in parallel. However I’m just going to show you yet another contrived example.

或者另一方面,您可以使用 sed,它旋转起来更轻

好吧,我的根本误解是假设最大命令行长度在 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 "  " [=10=]}}'

或 grep:

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

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