从并行产生的其他三个流中创建一个输出流


10

我有三种格式不同的数据。对于每种数据类型,都有一个Python脚本将其转换为统一的格式。

该Python脚本运行缓慢且受CPU限制(连接到多核计算机上的单个核),因此我想运行该脚本的三个实例(每种数据类型一个),并将其输出组合到中sort。基本上,等效于:

{ ./handle_1.py; ./handle_2.py; ./handle_3.py } | sort -n

但是,三个脚本并行运行。

我发现了一个问题,其中使用GNU split在处理该流的脚本的n个实例之间循环一些stdout流。

在拆分手册页中:

-n, --number=CHUNKS
          generate CHUNKS output files.  See below
CHUNKS  may be:
 N       split into N files based on size of input
 K/N     output Kth of N to stdout
 l/N     split into N files without splitting lines
 l/K/N   output Kth of N to stdout without splitting lines
 r/N     like 'l'  but  use  round  robin  distributio

因此,该r/N命令表示“ 不分割线 ”。

基于此,以下解决方案似乎应该是可行的:

split -n r/3 -u --filter="./choose_script" << EOF
> 1
> 2
> 3
> EOF

这在哪里choose_script

#!/bin/bash
{ read x; ./handle_$x.py; }

不幸的是,我看到了一些行的混合-以及许多不应该出现的换行符。

例如,如果我用执行此操作的一些简单bash脚本替换了我的Python脚本:

#!/bin/bash
# ./handle_1.sh
while true; echo "1-$RANDOM"; done;

#!/bin/bash
# ./handle_2.sh
while true; echo "2-$RANDOM"; done;

#!/bin/bash
# ./handle_3.sh
while true; echo "3-$RANDOM"; done;

我看到以下输出:

1-8394

2-11238
2-22757
1-723
2-6669
3-3690
2-892
2-312511-24152
2-9317
3-5981

这很烦人-根据我上面粘贴的手册页摘要,它应该保持行的完整性。

显然,如果我删除该-u参数,它会起作用,但随后会对其进行缓冲,并且会耗尽内存,因为它会缓冲除一个脚本之外的所有脚本的输出。

如果有人在这里有什么见识,将不胜感激。我不在这里。


在freenode上的#bash中,有些人建议我生成所有三个进程并对其进行后台处理,写入自定义FD,然后遍历这些FD并读取它们的行,但是我还没有弄清楚如何使其可行。还告诉我要查看coprocbash 中的内置函数,尽管我并没有真正看到它如何应用。
Cera 2012年

1
是否需要没有中间文件?你不能做job1.py > file1 & job2.py > file 2 & job3.py > file3 ; wait ; sort -n file1 file2 file3吗?
安格斯2012年

Answers:


2

尝试使用GNU parallel的-u选项。

echo "1\n2\n3" | parallel -u -IX ./handle_X.sh

这将并行运行它们,而不缓冲任何进程的整个过程。


我有点困惑-是XIX告诉-IX将要替换的标志,还是在应用-X标志,这似乎也具有相关含义?
Cera 2012年

mph 我正在这样做:parallel -u -X ./handle_{}.sh ::: "1" "2" "3",不幸的是,我仍然看到一些输出混乱。
Cera 2012年

前者:您也可以使用parallel -u ./handle_{}.sh,但我更喜欢更改它,因为花括号也具有将命令连接在一起的含义(如您的问题)。
flowblok 2012年

似乎对我有用,我的grep并没有受到任何干扰pastie.org/5113187(您使用的是测试bash脚本还是实际的Python脚本?)
flowblok 2012年

问题在于实际上并没有并行执行任何操作。我正在使用bash脚本-pastie.org/5113225
Cera,

2

尝试:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py

如果handle_1.py使用文件名:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

您不希望将输出混合在一起,因此不要使用-u。

如果要保留顺序(因此所有handle_1的输出都在handle_2之前,因此可以避免排序):

parallel -k  ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

如果仍然希望对其进行排序,则可以并行化排序并利用sort -m

parallel --files "./handle_{1}.py {2} | sort -n"  ::: 1 2 3 ::: files* | parallel -j1 -X sort -m

将$ TMPDIR设置为一个足以容纳输出的目录。


1
我确实希望输出“混合”-我只想确保最终输出中的每一行都是来自子流程之一的单行。如果不混合使用,系统将耗尽内存,缓冲尚未打印出的stdout流。
Cera 2012年

使用GNU Parallel,您将不会用完内存:它不会在内存中缓冲。您为什么认为它在内存中缓冲?
Ole Tange 2012年

2

也许我缺少了一些东西,但是你不能做:

(./handle_1.py & ./handle_2.py & ./handle_3.py) | sort -n

如果您希望不交错每个进程中的行,则可能更容易确保进程本身将它们完全写入,并可能禁用输出缓冲,write因为只要不大于s,就保证到管道的s是原子的PIPE_BUF。例如,您可以确保在写了一行或几行之后,确实使用了输出缓冲àla stdio和call fflush或任何等效的方法python

如果您无法修改python脚本,则可以执行以下操作:

lb() { grep --line-buffered '^'; }

(使用GNU grep)或:

lb() while IFS= read -r l; do printf '%s\n' "$l"; done

(如果命令输出不是文本,请参见下面注释中的注释)

并做:

(./handle_1.py | lb & ./handle_2.py | lb & ./handle_3.py | lb) | sort -n

避免这3个lb进程的另一种方法是将三个管道连接到一个使用select/的命令,poll以查看来自何处的某些输出并将其馈送到sort基于行的输出,但这需要一些编程。


wait我认为您需要在那里。
derobert

1
除非某些程序在退出之前关闭其stdout,否则不会,因为管道sort -n会一直存在,直到所有打开fd的程序都退出为止。
斯特凡Chazelas

确实,我测试过,您是正确的。
德罗伯特2012年

不,我仍然得到错误的输出。线混合在一起并交织在一起。
Cera 2012年

1
OK @Cerales,看到我的更新答案
斯特凡Chazelas

1

Flowbok的答案是正确的解决方案。奇怪的是,parallel如果将GNU 的输出直接输出到文件中,则该输出将被篡改-但是如果将其输出到tty,则不会。

幸运的是,script -c可以用来模仿tty。

仍然存在三个脚本:

#!/bin/bash
# handle_1.sh
while true; do echo "1-$RANDOM$RANDOM$RANDOM$RANDOM"; done

#!/bin/bash
# handle_2.sh
while true; do echo "2-$RANDOM$RANDOM$RANDOM$RANDOM"; done

#!/bin/bash
# handle_3.sh
while true; do echo "3-$RANDOM$RANDOM$RANDOM$RANDOM"; done

然后有一个文件封装了对并行的调用:

#!/bin/bash
# run_parallel.sh
parallel -u -I N ./handle_N.sh ::: "1" "2" "3"

然后我这样称呼它:

script -c ./run_parallel > output

输出中的行在不同脚本的输出之间逐行混合,但是它们不会在给定的行上混乱或交错。

怪异行为从parallel-我可以提交错误报告。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.