并行执行管道命令

Jer*_*nej 19 shell parallelism

考虑以下场景。我有两个程序 A 和 B。程序 A 输出到 stdout 字符串行,而程序 B 处理来自 stdin 的行。这两个程序的使用方式当然是:

foo@bar:~$ A | 乙

现在我注意到这只会吃掉一个核心;因此我想知道:

程序 A 和 B 是否共享相同的计算资源?如果是这样,有没有办法同时运行 A 和 B?

我注意到的另一件事是 A 运行得比 B 快得多,因此我想知道是否可以以某种方式运行更多 B 程序并让它们并行处理 A 输出的行。

也就是说,A 将输出它的行,并且将有 N 个程序 B 的实例将读取这些行(谁先读取它们)处理它们并将它们输出到 stdout。

所以我的最后一个问题是:

有没有办法将输出通过管道传送到多个 B 进程之间的 A 而不必处理竞争条件和其他可能出现的不一致?

Ole*_*nge 20

一个问题split --filter是输出可能会混淆,所以你从进程 1 得到半行,然后从进程 2 得到半行。

GNU Parallel 保证不会出现混淆。

所以假设你想做:

 A | B | C
Run Code Online (Sandbox Code Playgroud)

但是那个 B 非常慢,因此你想要并行化它。然后你可以这样做:

A | parallel --pipe B | C
Run Code Online (Sandbox Code Playgroud)

默认情况下,GNU Parallel 在 \n 上拆分,块大小为 1 MB。这可以通过 --recend 和 --block 进行调整。

您可以在以下位置找到有关 GNU Parallel 的更多信息:http : //www.gnu.org/s/parallel/

您可以在 10 秒内安装 GNU Parallel:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 883c667e01eed62f975ad28b6d50e22a
12345678 883c667e 01eed62f 975ad28b 6d50e22a
$ md5sum install.sh | grep cc21b4c943fd03e93ae1ae49e28573c0
cc21b4c9 43fd03e9 3ae1ae49 e28573c0
$ sha512sum install.sh | grep da012ec113b49a54e705f86d51e784ebced224fdf
79945d9d 250b42a4 2067bb00 99da012e c113b49a 54e705f8 6d51e784 ebced224
fdff3f52 ca588d64 e75f6033 61bd543f d631f592 2f87ceb2 ab034149 6df84a35
$ bash install.sh
Run Code Online (Sandbox Code Playgroud)

http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1上观看介绍视频

  • 对不起,我没有说清楚。安装方法 - 传递给 `sh` 的脚本 - 很棒。问题在于将其传递给 sh:*从站点下载并运行可执行代码*。请注意,也许我太偏执了,因为有人可能会反对定制的 RPM 或 DEB 基本上是一回事,甚至将代码张贴在要复制和粘贴的页面上也会导致人们盲目地这样做反正。 (4认同)
  • 虽然我强烈不同意安装方法:-),+1,因为你的解决方案解决了我的大部分问题。 (2认同)

LSe*_*rni 13

编写时A | B,两个进程已经并行运行。如果您认为它们只使用一个内核,那可能是因为 CPU 亲和性设置(也许有一些工具可以生成具有不同亲缘关系的进程)或因为一个进程不足以容纳整个内核,而系统“更喜欢”不分散计算。

要使用一个 A 运行多个 B,您需要一个工具,例如split带有以下--filter选项:

A | split [OPTIONS] --filter="B"
Run Code Online (Sandbox Code Playgroud)

但是,这很容易弄乱输出中的行顺序,因为 B 作业不会以相同的速度运行。如果这是一个问题,您可能需要将第 i 个输出重定向到一个中间文件,并在最后使用cat. 反过来,这可能需要相当大的磁盘空间。

存在其他选项(例如,您可以将 B 的每个实例限制为单个行缓冲输出,等到 B 的整个“回合”完成,运行等价于reduce tosplitmapcat临时输出),具有不同的效率水平。例如,刚刚描述的“round”选项将等待B最慢实例完成,因此它将极大地依赖于 B 的可用缓冲;[m]buffer可能有帮助,也可能没有,这取决于操作是什么。

例子

生成前 1000 个数字并并行计算行数:

seq 1 1000 | split -n r/10 -u --filter="wc -l"
100
100
100
100
100
100
100
100
100
100
Run Code Online (Sandbox Code Playgroud)

如果我们要“标记”这些行,我们会看到第一行被发送到进程#1,每第五行被发送到进程#5,依此类推。此外,在split产生第二个进程所需的时间内,第一个进程已经是其配额的好方法:

seq 1 1000 | split -n r/10 -u --filter="sed -e 's/^/$RANDOM - /g'" | head -n 10
19190 - 1
19190 - 11
19190 - 21
19190 - 31
19190 - 41
19190 - 51
19190 - 61
19190 - 71
19190 - 81
Run Code Online (Sandbox Code Playgroud)

在 2 核机器上执行时seqsplitwc进程共享内核;但是仔细观察,系统将前两个进程留在 CPU0 上,并将 CPU1 分配给工作进程:

A | split [OPTIONS] --filter="B"
Run Code Online (Sandbox Code Playgroud)

请特别注意,这split会占用大量 CPU。这将与 A 的需求成比例地减少;即,如果 A 是比 重的进程seq,则 的相对开销split将减少。但是,如果 A 是一个非常轻量级的过程并且 B 非常快(因此您不需要超过 2-3 个 B 来与 A 保持一致),那么与split(或通常的管道)并行化可能很不值得。

  • 这可能是因为*阻塞*。如果 B 比 A 重,则 A 无法发送其输出并减速。另一种可能性是A在其操作期间*屈服*给B(例如磁盘/网络)。在不同的系统上,您可能会看到 B 吞食了 100% 的 CPU1,而 A 被分配了 18% 的 CPU0。您可能需要 85/15 ~ 5.67 = 5 到 6 个 B 实例才能使单个 A 实例饱和单个核心。但是,I/O(如果存在)可能会扭曲这些值。 (2认同)