考虑以下场景。我有两个程序 A 和 B。程序 A 输出到 stdout 字符串行,而程序 B 处理来自 stdin 的字符串。这两个程序的使用方法当然是:
foo@bar:~$ A |乙
现在我注意到这只消耗了一个核心;因此我想知道:
程序 A 和 B 是否共享相同的计算资源?如果可以的话,有没有办法同时运行A和B?
我注意到的另一件事是 A 的运行速度比 B 快得多,因此我想知道是否可以以某种方式运行更多的 B 程序并让它们并行处理 A 输出的行。
也就是说,A 将输出其行,并且将有 N 个程序 B 实例读取这些行(无论谁先读取它们)处理它们并将它们输出到 stdout 上。
所以我的最后一个问题是:
有没有一种方法可以将输出通过管道传输到多个 B 进程中的 A,而不必考虑竞争条件和其他可能出现的不一致情况?
答案1
一个问题split --filter
是输出可能会混合,因此您会从进程 1 中获得半行,然后从进程 2 中获得半行。
GNU Parallel 保证不会出现混淆。
所以假设你想做:
A | B | C
但是 B 非常慢,因此您想要并行化它。然后你可以这样做:
A | parallel --pipe B | C
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
观看介绍视频http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
答案2
当你写的时候A | B
,两个进程已经并行运行。如果您看到它们仅使用一个核心,那可能是因为 CPU 关联性设置(也许有某种工具可以生成具有不同关联性的进程),或者因为一个进程不足以容纳整个核心,并且系统“宁愿”不分散计算。
要使用一个 A 运行多个 B,您需要一个工具,例如split
带有--filter
以下选项的工具:
A | split [OPTIONS] --filter="B"
然而,这可能会打乱输出中的行顺序,因为 B 作业不会以相同的速度运行。如果这是一个问题,您可能需要将第 B i 个输出重定向到中间文件,并在最后使用cat
.反过来,这可能需要相当大的磁盘空间。
存在其他选项(例如,您可以将 B 的每个实例限制为单个行缓冲输出,等到 B 的整个“轮”完成,运行相当于减少到split
的地图,以及cat
临时输出),具有不同的效率水平。例如,刚刚描述的“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
如果我们要“标记”这些行,我们会看到第一行被发送到进程 #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
当在 2 核机器上执行时,seq
,split
并且wc
进程共享核心;但仔细观察,系统将前两个进程留在 CPU0 上,并将 CPU1 分配给工作进程:
%Cpu0 : 47.2 us, 13.7 sy, 0.0 ni, 38.1 id, 1.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 15.8 us, 82.9 sy, 0.0 ni, 1.0 id, 0.0 wa, 0.3 hi, 0.0 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5314 lserni 20 0 4516 568 476 R 23.9 0.0 0:03.30 seq
5315 lserni 20 0 4580 720 608 R 52.5 0.0 0:07.32 split
5317 lserni 20 0 4520 576 484 S 13.3 0.0 0:01.86 wc
5318 lserni 20 0 4520 572 484 S 14.0 0.0 0:01.88 wc
5319 lserni 20 0 4520 576 484 S 13.6 0.0 0:01.88 wc
5320 lserni 20 0 4520 576 484 S 13.3 0.0 0:01.85 wc
5321 lserni 20 0 4520 572 484 S 13.3 0.0 0:01.84 wc
5322 lserni 20 0 4520 576 484 S 13.3 0.0 0:01.86 wc
5323 lserni 20 0 4520 576 484 S 13.3 0.0 0:01.86 wc
5324 lserni 20 0 4520 576 484 S 13.3 0.0 0:01.87 wc
请特别注意,它split
占用了大量的 CPU。这将根据 A 的需求按比例减少;即,如果 A 是一个比 更重的进程seq
,则 的相对开销split
将会减少。但如果 A 是一个非常轻量级的进程,并且 B 非常快(因此您不需要超过 2-3 个 B 来与 A 保持一致),则与split
(或一般情况下的管道)并行化可能不值得。