【问题标题】:What's hanging up this use of map.pool in python?在 python 中使用 map.pool 的原因是什么?
【发布时间】:2011-10-13 17:15:11
【问题描述】:

我有一个正在运行的命令行程序,我将文本作为参数输入:

somecommand.exe

它会运行一段时间(通常是一个小时到几个小时的好几分之一),然后将结果写入多个文本文件。我正在尝试编写一个脚本来同时启动其中的几个,使用多核机器上的所有内核。在其他操作系统上我会分叉,但这并没有在 Windows 的许多脚本语言中实现。 Python 的多处理似乎可以解决问题,所以我想我会尝试一下,尽管我根本不知道 python。我希望有人能告诉我我做错了什么。

我编写了一个脚本(如下),我指向一个目录,如果找到可执行文件和输入文件,然后使用 pool.map 和一个 n 池启动它们,并使用调用来启动它们。我看到的是,最初(启动第一组 n 个进程)看起来不错,100% 使用 n 个内核。但随后我看到进程处于空闲状态,不使用或仅使用百分之几的 CPU。那里总是有 n 个进程,但它们做的并不多。当他们去写入输出数据文件时似乎会发生这种情况,一旦开始,一切都会陷入困境,整体核心利用率范围从百分之几到偶尔达到 50-60% 的峰值,但永远不会接近 100%。

如果我可以附上它(编辑:我不能,至少现在不能)这里是进程的运行时间图。较低的曲线是当我打开 n 个命令提示符并手动保持 n 个进程一次运行时,很容易将计算机保持在 100% 附近。 (这条线是规则的,在 32 个不同的进程中从接近 0 小时慢慢增加到 0.7 小时,改变一个参数。)上面的线是这个脚本的某个版本的结果——运行时间平均增加了大约 0.2 小时,并且是更不可预测,就像我在底线中添加了 0.2 + 一个随机数。

这是剧情的链接: Run time plot

编辑:现在我想我可以添加情节了。

我做错了什么?

from multiprocessing import Pool, cpu_count, Lock
from subprocess import call
import glob, time, os, shlex, sys
import random

def launchCmd(s):
    mypid = os.getpid()
    try:
        retcode = call(s, shell=True)
        if retcode < 0:
            print >>sys.stderr, "Child was terminated by signal", -retcode
        else:
            print >>sys.stderr, "Child returned", retcode
    except OSError, e:
        print >>sys.stderr, "Execution failed:", e

if __name__ == '__main__':

    # ******************************************************************
    # change this to the path you have the executable and input files in
    mypath = 'E:\\foo\\test\\'
    # ******************************************************************

    startpath = os.getcwd()
    os.chdir(mypath)
    # find list of input files
    flist = glob.glob('*_tin.txt')
    elist = glob.glob('*.exe')
    # this will not act as expected if there's more than one .exe file in that directory!
    ex = elist[0] + ' < '

    print
    print 'START'
    print 'Path: ', mypath
    print 'Using the executable: ', ex
    nin = len(flist)
    print 'Found ',nin,' input files.'
    print '-----'
    clist = [ex + s for s in flist]
    cores = cpu_count()
    print 'CPU count ', cores
    print '-----'

    # ******************************************************
    # change this to the number of processes you want to run
    nproc = cores -1
    # ******************************************************

    pool = Pool(processes=nproc, maxtasksperchild=1)    # start nproc worker processes
    # mychunk = int(nin/nproc)      # this didn't help
    # list.reverse(clist)           # neither did this, or randomizing the list
    pool.map(launchCmd, clist)      # launch processes
    os.chdir(startpath)             # return to original working directory
    print 'Done'

【问题讨论】:

  • 你看起来真的很清楚自己在做什么;对于自称是新手的人来说,这看起来像是很好的 Python。问题一:CPU空闲时,硬​​盘是不是超级忙?从理论上讲,如果您的进程产生大量输出,则在等待磁盘写入所有内容时,这些进程可能大部分时间都是空闲的。如果由于某种原因缓存不起作用,则尤其如此。
  • 当 CPU 使用率下降时(这发生在第一个进程开始写入其输出时),磁盘活动似乎(如资源监视器报告的那样)出现峰值,然后保持在 100% 附近,直到好在所有过程完成之后。磁盘队列也达到了 50。我很好奇为什么会出现这种情况,但当我从多个命令行手动执行相同的命令时却不是——看起来确实有些东西被共享了(很糟糕)。
  • 我应该补充一点:我不在乎这些进程以什么顺序完成。在示例中,我现在正在尝试最短的先运行。随机化或颠倒顺序可能会有所帮助,但影响不大。

标签: python windows multiprocessing pool


【解决方案1】:

进程是否有机会尝试写入公共文件?在 Linux 下,它可能会正常工作,破坏数据但不会减慢速度;但在 Windows 下,一个进程可能会获取文件,而所有其他进程可能会挂起等待文件可用。

如果您用一些使用 CPU 但不写入磁盘的愚蠢任务替换您的实际任务列表,问题是否会重现?例如,您可能有计算某个大文件的 md5sum 的任务;一旦文件被缓存,其他任务将是纯 CPU,然后单行输出到标准输出。或者计算一些昂贵的函数之类的。

【讨论】:

    【解决方案2】:

    认为我知道这一点。当您调用map 时,它会将任务列表分解为每个进程的“块”。默认情况下,它使用足够大的块,它可以向每个进程发送一个。这是假设所有任务都需要大约相同的时间来完成。

    在您的情况下,完成任务可能需要非常不同的时间。因此,一些工人比其他工人先完成,而这些 CPU 则处于空闲状态。如果是这种情况,那么这应该可以按预期工作:

    pool.map(launchCmd, clist, chunksize=1)
    

    效率较低,但这应该意味着每个工人在完成之前会获得更多任务,直到它们全部完成。

    【讨论】:

    • 不幸的是,这似乎没有什么不同。
    猜你喜欢
    • 2017-04-10
    • 2010-12-04
    • 1970-01-01
    • 2021-07-27
    • 1970-01-01
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 2020-09-24
    相关资源
    最近更新 更多