【问题标题】:Using xargs for parallel Python scripts将 xargs 用于并行 Python 脚本
【发布时间】:2017-02-04 10:32:55
【问题描述】:

我目前有一个带有两个嵌套循环的 bash 脚本 script.sh。第一个枚举a的可能值,第二个枚举b的可能值,例如

#!/bin/sh
for a in {1..10}
do
    for b in {1..10}
    do
        nohup python script.py $a $b &
    done
done

因此,这会产生 100 个运行 script.py 的 Python 进程,每个 (a,b) 对对应一个。但是,我的机器只有 5 个内核,所以我想将进程数限制在 5 个,以避免颠簸/浪费切换。目标是我总是运行 5 个进程,直到所有 100 个进程都完成。

xargs 似乎是一种方法,但我不知道如何将这些参数传递给 xargs。我检查了其他类似的问题,但对周围的 bash 行话不够了解,无法知道发生了什么。比如我试过

seq 1 | xargs -i --max-procs=5 bash script.sh

但这似乎没有任何作用 - script.sh 像以前一样运行并且仍然产生 100 个进程。

我认为我误解了 xargs 的工作原理。

谢谢!

【问题讨论】:

  • xargs 不能也不会改变其子进程启动自己的子进程的方式;相反,它提供了对其如何启动子流程的控制。
  • 对于 Python,我实际上建议使用 Python multiprocessing 模块而不是 xargs。或者 GNU parallels 实用程序。 docs.python.org/3/library/multiprocessing.html
  • @Wolph,我绝对同意:多处理。至于 GNU 并行——它是一个比xargs 更大、更复杂、更复杂的代码库,相比之下,xargs 是一个简单的模型。鉴于 Pythonist 对简单性的偏好,我认为 xargs 是两者之间的明显选择。 :)
  • @CharlesDuffy 这很公平。但与此同时,它可以更好地控制并行进程的执行方式,并且比 GNU 特定的 xargs 扩展更具可移植性:)
  • @Wolph,不是 GNU 特定的; --max-procs 是 GNUism,但 -P 也适用于 OS X 和现代 FreeBSD。

标签: python bash parallel-processing xargs


【解决方案1】:

这实际上看起来更像:

#!/bin/bash
for a in {1..10}; do
  for b in {1..10}; do
    printf '%s\0' "$a" "$b"
  done
done | xargs -0 -x -n 2 -P 5 python script.py

请注意,没有nohup,也没有任何& -- 来跟踪并发调用的数量,xargs 需要直接执行 Python 脚本,并且该进程在完成之前不能退出。

非标准(但广泛使用)-0 扩展要求输入为以 NUL 分隔的形式(使用 printf '%s\0' 创建);这可以确保参数具有空格、引号、反斜杠等的正确行为。

同样非标准的-P 5 设置了最大进程数(在某种程度上比--max-procs=5 更便携,GNU 支持但现代 BSD xargs 不支持)。

-n 2 表示 Python 脚本的每个实例仅接收两个参数,因此每对输入开始一个。

-x(与-n 2 一起使用)表示如果不能为单个 Python 实例提供两个参数(例如,如果参数太长以至于不能在单个命令中使用行),这应该被视为失败,而不是调用只有一个参数的 Python 实例。

【讨论】:

  • 在 Cygwin 中失败:-bash: fi: 第 4 行:{1..10} 中 b 的意外标记 for' -bash: fi: line 4: 附近出现语法错误'
  • @OleTange,我错过了一些; dos——现在已经修复了。
【解决方案2】:

GNU Parallel 正是为这些类型的工作而设计的:

parallel python script.py ::: {1..10} ::: {1..10}

如果您需要将 $a 和 $b 放在不同的位置,您可以使用 {1} 和 {2} 来引用两个输入源:

parallel python script.py --option-a {1} --option-b {2} ::: {1..10} ::: {1..10}

GNU Parallel 是一种通用的并行化器,可以轻松地在同一台机器上或在您可以通过 ssh 访问的多台机器上并行运行作业。它通常可以替换 for 循环。

如果您想在 4 个 CPU 上运行 32 个不同的作业,那么并行化的直接方法是在每个 CPU 上运行 8 个作业:

GNU Parallel 会在完成后生成一个新进程 - 保持 CPU 处于活动状态,从而节省时间:

安装

如果没有为您的发行版打包 GNU Parallel,您可以进行个人安装,这不需要 root 访问权限。这样做可以在 10 秒内完成:

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash

有关其他安装选项,请参阅http://git.savannah.gnu.org/cgit/parallel.git/tree/README

了解详情

查看更多示例:http://www.gnu.org/software/parallel/man.html

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

浏览教程:http://www.gnu.org/software/parallel/parallel_tutorial.html

注册电子邮件列表以获得支持:https://lists.gnu.org/mailman/listinfo/parallel

【讨论】:

    【解决方案3】:

    如果你使用 bash,那么以下应该可以工作:

    #!/bin/bash
    
    for a in {1..10}
    do
        for b in {1..10}
        do
            if [ `jobs | wc -l` -lt 6 ]; then # less than 6 background jobs
                nohup python script.py $a $b &
            else
                wait -n   # wait for any background job to terminate
            fi
        done
    done
    

    【讨论】:

    • 如果你想让waitjobs 工作,请取出nohup
    • @CharlesDuffy 它也适用于nohup。使用nohup sleep 5& + wait 测试。
    • 当然——它是 shell-builtin 表亲 disown 会产生这种效果。
    • ...wait -n 不是最近才添加到 shell 中的吗?我想说它是 4.0 后的某个地方。
    猜你喜欢
    • 2021-12-18
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 2012-08-04
    • 2010-12-08
    相关资源
    最近更新 更多