为什么“读取”文件的每一行比“xargs”快得多

Fra*_*ien 4 shell bash xargs

我有一个包含 30M 行的大输入文件,在\r\n. 我决定做一些愚蠢的事情,并通过read -r与xargs(剥离第\r一个,因为xargs似乎无法拆分多个字符)比较计算所有行的速度。这是我的两个命令:

time tr -d '\r' < input.txt | xargs -P 1 -d '\n' -I {} echo "{}" | wc -l
Run Code Online (Sandbox Code Playgroud)
time while read -r p || [ -n "$p" ]; do echo "$p"; done < input.txt | wc -l
Run Code Online (Sandbox Code Playgroud)

在这里,第二种解决方案要快得多。这是为什么?

请注意,我知道这不是计算文件行数的正确方法。这个问题只是出于我观察的兴趣。

Sté*_*las 10

是的,当你怀疑xargs -P 1 -d '\n' -I {} echo "{}",这实际上是一样xargs -rd '\n' -n1的echo是默认的命令,将创建一个进程并执行独立echo的子过程,同时在父等待其终止对输入的每一行。

所以这比在同一个 shell 进程中使用低效的read内置函数和echo内置函数要多得多。

如果xargs您使用 busybox而不是 GNU ,xargs它(至少在某些配置和最新版本中)将在echo内部调用 busybox而无需分叉进程,您会注意到它会比bash循环快得多。

要进行更相关的比较,您应该比较:

tr -d '\r' | xargs -rd'\n' -n1
Run Code Online (Sandbox Code Playgroud)

和

tr -d '\r' |
  while IFS= read -r line || [ -n "$line" ]; do
   /bin/echo "$line"
  done
Run Code Online (Sandbox Code Playgroud)

这可能会给您类似的结果,因为大部分时间将花费在分叉进程和执行独立的echo.

在这里,关于输出seq 3e7和测量pv -al > /dev/null(以每秒a平均lines 数来测量吞吐量),我绕过:

  • 1.12M/s for busybox xargs
  • 带有内置回显的 bash 循环为 70k/s
  • 860/s for GNU xargs
  • 850/s for bash loop with /bin/echo