【问题标题】:profiling python multiprocessing pool分析 python 多处理池
【发布时间】:2015-09-11 10:21:51
【问题描述】:

我有一些代码使用 Python 的 multiprocessing 模块中的 Pool。性能不是我所期望的,我想分析代码以找出发生了什么。我遇到的问题是每个作业的分析输出都会被覆盖,我无法积累大量的统计数据。

例如,用:

import multiprocessing as mp
import cProfile
import time
import random

def work(i):
    x = random.random()
    time.sleep(x)
    return (i,x)

def work_(args):
    out = [None]
    cProfile.runctx('out[0] = work(args)', globals(), locals(),
                    'profile-%s.out' % mp.current_process().name)
    return out[0]

pool = mp.Pool(10)

for i in pool.imap_unordered(work_, range(100)):
    print(i)

我只获得“最后一个”工作的统计数据,这可能不是计算要求最高的工作。我想我需要将统计信息存储在某个地方,然后仅在清理池时才将它们写出来。

【问题讨论】:

    标签: python profiling python-multiprocessing


    【解决方案1】:

    我的解决方案包括更长时间地持有配置文件对象,并且只在“结束”时将其写出。在better elsewhere 中描述了挂钩到池拆卸,但涉及使用Finalize 对象在适当的时间显式执行dump_stats()

    这也让我可以用我之前使用的runctx 来整理尴尬的work_ 蹦床。

    import multiprocessing as mp
    import cProfile
    import time
    import random
    
    def work(i):
        # enable profiling (refers to the global object below)
        prof.enable()
        x = random.random()
        time.sleep(x)
        # disable so we don't profile the Pool
        prof.disable()
        return (i,x)
    
    # Initialise a good profile object and make sure it gets written during Pool teardown
    def _poolinit():
        global prof
        prof = cProfile.Profile()
        def fin():
            prof.dump_stats('profile-%s.out' % mp.current_process().pid)
    
        mp.util.Finalize(None, fin, exitpriority=1)
    
    # create our pool
    pool = mp.Pool(10, _poolinit)
    
    for i in pool.imap_unordered(work, range(100)):
        print(i)
    

    加载输出显示确实记录了多次调用:

    > p = pstats.Stats("profile-ForkPoolWorker-5.out")
    > p.sort_stats("time").print_stats(10)
    Fri Sep 11 12:11:58 2015    profile-ForkPoolWorker-5.out
    
             30 function calls in 4.684 seconds
    
       Ordered by: internal time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
           10    4.684    0.468    4.684    0.468 {built-in method sleep}
           10    0.000    0.000    0.000    0.000 {method 'random' of '_random.Random' objects}
           10    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    

    【讨论】:

      【解决方案2】:

      根据docs,您可以使用pid 属性来获取每个输出文件的唯一名称

      cProfile.runctx('out[0] = work(args)', globals(), locals(),
                      'profile-%s-%s.out' % (mp.current_process().pid, datetime.now().isoformat()))
      

      【讨论】:

      • 问题是runctx 在每次调用时都会覆盖输出,因为它会执行多次,您只能获得每个进程的“最后一个”作业——即将发布一个半工作解决方案
      • 啊好吧 - 它被覆盖每个进程 - 为什么不只是添加一个时间戳?
      • 因为我在现实生活中有 很多 个工作正在执行,我不想要数百万个小型配置文件
      猜你喜欢
      • 2021-07-24
      • 2020-08-14
      • 2016-11-10
      • 2016-07-10
      • 2017-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多