Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python多处理:我可以使用更新的全局变量重用进程(已经并行化的函数)吗?_Python_Python 3.x_Multiprocessing - Fatal编程技术网

Python多处理:我可以使用更新的全局变量重用进程(已经并行化的函数)吗?

Python多处理:我可以使用更新的全局变量重用进程(已经并行化的函数)吗?,python,python-3.x,multiprocessing,Python,Python 3.x,Multiprocessing,首先,让我向您展示我当前的设置: import multiprocessing.pool from contextlib import closing import os def big_function(param): process(another_module.global_variable[param]) def dispatcher(): # sharing read-only global variable taking benefit from Unix

首先,让我向您展示我当前的设置:

import multiprocessing.pool
from contextlib import closing
import os

def big_function(param):
   process(another_module.global_variable[param])


def dispatcher():
    # sharing read-only global variable taking benefit from Unix
    # which follows policy copy-on-update
    # https://stackoverflow.com/questions/19366259/
    another_module.global_variable = huge_list

    # send indices
    params = range(len(another_module.global_variable))

    with closing(multiprocessing.pool.Pool(processes=os.cpu_count())) as p:
        multiprocessing_result = list(p.imap_unordered(big_function, params))

    return multiprocessing_result
在这里,我使用了在创建进程池之前更新的共享变量,该进程池包含大量数据,这确实提高了我的速度,所以它现在似乎没有被修改。此外,此变量属于导入模块的范围(如果重要)

当我尝试创建如下设置时:

another_module.global_variable = []

p = multiprocessing.pool.Pool(processes=os.cpu_count())

def dispatcher():
    # sharing read-only global variable taking benefit from Unix
    # which follows policy copy-on-update
    # https://stackoverflow.com/questions/19366259/
    another_module_global_variable = huge_list

    # send indices
    params = range(len(another_module.global_variable))

    multiprocessing_result = list(p.imap_unordered(big_function, params))

    return multiprocessing_result  
import multiprocessing.pool
from contextlib import closing
import os

def big_function(params):
   results = []
   for p in params:
       results.append(process(another_module.global_variable[p]))
   return results

def dispatcher():
    # sharing read-only global variable taking benefit from Unix
    # which follows policy copy-on-update
    # https://stackoverflow.com/questions/19366259/
    another_module.global_variable = huge_list

    # send indices
    params = range(len(another_module.global_variable))

    with closing(multiprocessing.pool.Pool(processes=os.cpu_count())) as p:
        multiprocessing_result = list(p.imap_unordered(big_function, params, chunksize=10))

    return multiprocessing_result
p
“记住”全局共享列表为空,从调度程序内部调用时拒绝使用新数据


现在问题来了:在上面的第一个设置中,在8个核上处理约600个数据对象,我的并行计算运行8秒,而单线程运行12秒

这就是我的想法:只要多处理pickle数据,我每次都需要重新创建进程,我就需要pickle function
big_function()
,所以我在这方面浪费了时间。使用全局变量部分解决了数据的问题(但我仍然需要在每次更新时重新创建池)


如何处理
big_function()
(这取决于其他模块、numpy等的许多其他函数)的实例?我是否可以一次性创建其拷贝的
os.cpu\u count()
,并以某种方式将新数据输入拷贝中并接收结果,从而重用工作人员?

仅讨论“记住”问题:

another_module.global_variable = []
p = multiprocessing.pool.Pool(processes=os.cpu_count())

def dispatcher():
    another_module_global_variable = huge_list
    params = range(len(another_module.global_variable))
    multiprocessing_result = list(p.imap_unordered(big_function, params))
    return multiprocessing_result 
问题似乎是在创建
实例时

为什么呢

这是因为,当您创建
池的实例时,它确实设置了工作线程的数量(默认情况下等于CPU内核的数量),并且它们都在那时启动(分叉)。这意味着工人拥有父级全局状态的副本(以及
另一个\u模块。全局\u变量
以及其他所有内容),并且在更新
另一个\u模块的值时,使用写时复制策略。全局\u变量
可以在父级进程中更改它。工人有对旧值的引用。这就是你对它有问题的原因

这里有几个链接可以给你更多的解释:和

这里是一个小片段,您可以在其中切换全局变量值更改的行和流程启动的行,并检查子流程中打印的内容

from __future__ import print_function
import multiprocessing as mp

glob = dict()
glob[0] = [1, 2, 3]


def printer(a):
    print(globals())
    print(a, glob[0])


if __name__ == '__main__':
    p = mp.Process(target=printer, args=(1,))
    p.start()
    glob[0] = 'test'
    p.join()
这是Python2.7代码,但它也适用于Python3.6

这个问题的解决方案是什么

好吧,回到第一个解决方案。更新导入模块变量的值,然后创建进程池


现在,真正的问题是缺乏加速

下面是关于函数如何被pickle的有趣部分:

请注意,函数(内置和用户定义)由“完全 限定的“名称”引用,而不是按值。这意味着只有 函数名与模块名一起被pickle 函数在中定义。既不是函数的代码,也不是它的任何 函数属性被pickle。因此,定义模块必须是 可在取消勾选环境中导入,且模块必须包含 指定的对象,否则将引发异常

这意味着您的函数酸洗不应该是一个浪费时间的过程,或者至少本身不应该。导致加速不足的原因是,对于传递给
imap\u unordered
调用的列表中的约600个数据对象,您将它们中的每一个传递给工作进程。再次说明,
multiprocessing.Pool
的底层实现可能是此问题的原因

如果深入研究
多处理.Pool
实现,您将看到使用
队列的两个
线程正在处理父进程和所有子进程(工作进程)之间的通信。由于这一点,以及所有进程不断需要函数的参数并不断返回响应,最终导致父进程非常繁忙。这就是为什么“很多”时间都花在“分派”工作上,在工作进程之间传递数据

怎么办

随时尝试增加辅助进程中进程的数据对象数量。在您的示例中,您一个接一个地传递数据对象,并且可以确保每个辅助进程在任何时候都只处理一个数据对象。为什么不增加传递给辅助进程的数据对象的数量?这样,您可以使每个流程更加繁忙,处理10个、20个甚至更多的数据对象。从我所看到的,
imap_unordered
有一个
chunksize
参数。默认设置为
1
。试着增加它。大概是这样的:

another_module.global_variable = []

p = multiprocessing.pool.Pool(processes=os.cpu_count())

def dispatcher():
    # sharing read-only global variable taking benefit from Unix
    # which follows policy copy-on-update
    # https://stackoverflow.com/questions/19366259/
    another_module_global_variable = huge_list

    # send indices
    params = range(len(another_module.global_variable))

    multiprocessing_result = list(p.imap_unordered(big_function, params))

    return multiprocessing_result  
import multiprocessing.pool
from contextlib import closing
import os

def big_function(params):
   results = []
   for p in params:
       results.append(process(another_module.global_variable[p]))
   return results

def dispatcher():
    # sharing read-only global variable taking benefit from Unix
    # which follows policy copy-on-update
    # https://stackoverflow.com/questions/19366259/
    another_module.global_variable = huge_list

    # send indices
    params = range(len(another_module.global_variable))

    with closing(multiprocessing.pool.Pool(processes=os.cpu_count())) as p:
        multiprocessing_result = list(p.imap_unordered(big_function, params, chunksize=10))

    return multiprocessing_result
两条建议:

  • 我看到您创建了
    params
    作为索引列表,用于在
    big_函数
    中选择特定的数据对象。您可以创建表示第一个和最后一个索引的元组,并将它们传递给
    big\u函数
    。这是一种增加工作量的方法。这是我上面提出的另一种方法
  • 除非您明确希望拥有
    池(processs=os.cpu\u count())
    ,否则可以忽略它。默认情况下,它采用CPU核心数
  • 很抱歉,答案太长,或者有任何可能的打字错误