Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/324.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 如何在for循环中使用多处理并行化对同一函数的两个调用(参数不同)?_Python_Python 3.x_Ubuntu_For Loop_Multiprocessing - Fatal编程技术网

Python 如何在for循环中使用多处理并行化对同一函数的两个调用(参数不同)?

Python 如何在for循环中使用多处理并行化对同一函数的两个调用(参数不同)?,python,python-3.x,ubuntu,for-loop,multiprocessing,Python,Python 3.x,Ubuntu,For Loop,Multiprocessing,在for循环中,我调用一个函数两次,但参数集argSet1和argSet2在for循环的每次迭代中都会发生变化。我想并行化这个操作,因为一组参数会导致被调用函数运行更快,而另一组参数会导致函数运行缓慢。注意,对于这个操作,我不希望有两个for循环。我还有另一个要求:这些函数中的每一个都将执行一些并行操作,因此我不想让任何带有argSet1或argSet2的函数运行多次,因为我的计算资源有限。确保具有两个参数集的函数都在运行将帮助我尽可能多地利用CPU核心。以下是在没有并行化的情况下如何正常工作:

在for循环中,我调用一个函数两次,但参数集argSet1和argSet2在for循环的每次迭代中都会发生变化。我想并行化这个操作,因为一组参数会导致被调用函数运行更快,而另一组参数会导致函数运行缓慢。注意,对于这个操作,我不希望有两个for循环。我还有另一个要求:这些函数中的每一个都将执行一些并行操作,因此我不想让任何带有argSet1或argSet2的函数运行多次,因为我的计算资源有限。确保具有两个参数集的函数都在运行将帮助我尽可能多地利用CPU核心。以下是在没有并行化的情况下如何正常工作:

def myFunc(arg1, arg2):
    if arg1:
        print ('do something that does not take too long')
    else:
       print ('do something that takes long')

for i in range(10):
    argSet1 = arg1Storage[i]
    argSet1 = arg2Storage[i]
    myFunc(argSet1)
    myFunc(argSet2)
这绝对不会利用我拥有的计算资源。以下是我对并行化操作的尝试:

from multiprocessing import Process

def myFunc(arg1, arg2):
    if arg1:
        print ('do something that does not take too long')
    else:
       print ('do something that takes long')

for i in range(10):
    argSet1 = arg1Storage[i]
    argSet1 = arg2Storage[i]
    p1 = Process(target=myFunc, args=argSet1)
    p1.start()
    p2 = Process(target=myFunc, args=argSet2)
    p2.start()
然而,通过这种方式,每个函数及其各自的参数将被调用10次,并且速度会变得非常缓慢。鉴于我对多处理的了解有限,我尝试通过将p1.join和p2.join添加到for循环的末尾来进一步改进,但这仍然会导致速度减慢,因为p1的速度要快得多,并且要等到p2完成。我还考虑过使用multiprocessing.Value与函数进行一些通信,但随后我必须在函数内部为每个函数调用添加一个while循环,这会再次降低所有操作的速度。我想知道是否有人能提供一个实用的解决方案?

您可能需要使用多进程池,并将myFunc映射到其中,如下所示:

from multiprocessing import Pool
import time

def myFunc(arg1, arg2):
    if arg1:
        print ('do something that does not take too long')
        time.sleep(0.01)
    else:
       print ('do something that takes long')
       time.sleep(1)

def wrap(args):
    return myFunc(*args)

if __name__ == "__main__":
    p = Pool()
    argStorage = [(True, False), (False, True)] * 12
    p.map(wrap, argStorage)
我添加了一个wrap函数,因为传递给p.map的函数必须接受一个参数。如果在您的情况下可能的话,您也可以调整myFunc以接受元组

我的样本包含24项,其中12项需要1秒处理,12项需要10毫秒完成。总的来说,此脚本运行时间为3-4秒,我有4个内核。

您可能需要使用多进程。进程池,并将myFunc映射到其中,如下所示:

from multiprocessing import Pool
import time

def myFunc(arg1, arg2):
    if arg1:
        print ('do something that does not take too long')
        time.sleep(0.01)
    else:
       print ('do something that takes long')
       time.sleep(1)

def wrap(args):
    return myFunc(*args)

if __name__ == "__main__":
    p = Pool()
    argStorage = [(True, False), (False, True)] * 12
    p.map(wrap, argStorage)
我添加了一个wrap函数,因为传递给p.map的函数必须接受一个参数。如果在您的情况下可能的话,您也可以调整myFunc以接受元组


我的样本包含24项,其中12项需要1秒处理,12项需要10毫秒完成。这个脚本总共在3-4秒内运行,我有4个内核。

一个可能的实现如下:

import concurrent.futures
import math

list_of_args = [arg1, arg2]

def my_func(arg):
    ....
    print ('do something that takes long')

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for arg, result in zip(list_of_args, executor.map(is_prime, list_of_args)):
            print('my_func({0}) => {1}'.format(arg, result))

executor.map与内置函数类似,map方法允许对提供的函数进行多次调用,并将iterable中的每个项传递给该函数。

一种可能的实现如下所示:

import concurrent.futures
import math

list_of_args = [arg1, arg2]

def my_func(arg):
    ....
    print ('do something that takes long')

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for arg, result in zip(list_of_args, executor.map(is_prime, list_of_args)):
            print('my_func({0}) => {1}'.format(arg, result))
executor.map与内置函数类似,map方法允许对提供的函数进行多次调用,并将iterable中的每个项传递给该函数。

由于我在补丁中构建了此答案,请向下滚动以找到此问题的最佳解决方案

您需要确切地指定希望运行的方式。据我所知,您最多需要运行两个进程,但至少也要运行两个进程。同时,你也不希望沉重的电话阻碍快速的电话。一种简单的非最佳运行方式是:

from multiprocessing import Process

def func(counter,somearg):
    j = 0
    for i in range(counter): j+=i
    print(somearg)

def loop(counter,arglist):
    for i in range(10):
        func(counter,arglist[i])

heavy = Process(target=loop,args=[1000000,['heavy'+str(i) for i in range(10)]])
light = Process(target=loop,args=[500000,['light'+str(i) for i in range(10)]])
heavy.start()
light.start()
heavy.join()
light.join()
此处的输出用于一个示例运行:

light0
heavy0
light1
light2
heavy1
light3
light4
heavy2
light5
light6
heavy3
light7
light8
heavy4
light9
heavy5
heavy6
heavy7
heavy8
heavy9
您可以看到最后一个部分是次优的,因为您有一系列繁重的运行—这意味着只有一个进程而不是两个进程

如果您可以估计这个繁重的进程运行多长时间,那么这是一个简单的优化方法。如果它的速度是这里的两倍,只需先运行7次重迭代,加入轻流程,然后让它运行另外3次

另一种方法是成对运行重进程,因此首先有3个进程,直到快速进程结束,然后继续运行2个进程

主要的一点是将重调用和轻调用完全分离到另一个进程-因此,当快速调用一个接一个地快速完成时,您可以处理慢调用。一旦这个快速结束,你想继续做什么取决于你自己,但我认为现在估计如何打破这些沉重的电话已经足够了。以下是我的例子:

from multiprocessing import Process

def func(counter,somearg):
    j = 0
    for i in range(counter): j+=i
    print(somearg)

def loop(counter,amount,arglist):
    for i in range(amount):
        func(counter,arglist[i])

heavy1 = Process(target=loop,args=[1000000,7,['heavy1'+str(i) for i in range(7)]])
light = Process(target=loop,args=[500000,10,['light'+str(i) for i in range(10)]])
heavy2 = Process(target=loop,args=[1000000,3,['heavy2'+str(i) for i in range(7,10)]])
heavy1.start()
light.start()
light.join()
heavy2.start()
heavy1.join()
heavy2.join()
输出:

light0
heavy10
light1
light2
heavy11
light3
light4
heavy12
light5
light6
heavy13
light7
light8
heavy14
light9
heavy15
heavy27
heavy16
heavy28
heavy29
更好的利用。当然,您可以通过为慢进程运行共享一个队列来提高这一点,这样当快进程完成时,他们可以作为慢队列上的工作人员加入,但是对于仅两个不同的调用,这可能是过度的,尽管使用。最佳解决方案:

这是您想要的最佳且可扩展的解决方案。输出:

Worker 0 : light0
Worker 0 : light1
Worker 1 : heavy0
Worker 1 : light2
Worker 0 : heavy1
Worker 0 : light3
Worker 1 : heavy2
Worker 1 : light4
Worker 0 : heavy3
Worker 0 : light5
Worker 1 : heavy4
Worker 1 : light6
Worker 0 : heavy5
Worker 0 : light7
Worker 1 : heavy6
Worker 1 : light8
Worker 0 : heavy7
Worker 0 : light9
Worker 1 : heavy8
Worker 0 : heavy9
因为我在补丁中构建了这个答案,所以向下滚动寻找这个问题的最佳解决方案

您需要确切地指定希望运行的方式。据我所知,您最多需要运行两个进程,但至少也要运行两个进程。 同时,你也不希望沉重的电话阻碍快速的电话。一种简单的非最佳运行方式是:

from multiprocessing import Process

def func(counter,somearg):
    j = 0
    for i in range(counter): j+=i
    print(somearg)

def loop(counter,arglist):
    for i in range(10):
        func(counter,arglist[i])

heavy = Process(target=loop,args=[1000000,['heavy'+str(i) for i in range(10)]])
light = Process(target=loop,args=[500000,['light'+str(i) for i in range(10)]])
heavy.start()
light.start()
heavy.join()
light.join()
此处的输出用于一个示例运行:

light0
heavy0
light1
light2
heavy1
light3
light4
heavy2
light5
light6
heavy3
light7
light8
heavy4
light9
heavy5
heavy6
heavy7
heavy8
heavy9
您可以看到最后一个部分是次优的,因为您有一系列繁重的运行—这意味着只有一个进程而不是两个进程

如果您可以估计这个繁重的进程运行多长时间,那么这是一个简单的优化方法。如果它的速度是这里的两倍,只需先运行7次重迭代,加入轻流程,然后让它运行另外3次

另一种方法是成对运行重进程,因此首先有3个进程,直到快速进程结束,然后继续运行2个进程

主要的一点是将重调用和轻调用完全分离到另一个进程-因此,当快速调用一个接一个地快速完成时,您可以处理慢调用。一旦这个快速结束,你想继续做什么取决于你自己,但我认为现在估计如何打破这些沉重的电话已经足够了。以下是我的例子:

from multiprocessing import Process

def func(counter,somearg):
    j = 0
    for i in range(counter): j+=i
    print(somearg)

def loop(counter,amount,arglist):
    for i in range(amount):
        func(counter,arglist[i])

heavy1 = Process(target=loop,args=[1000000,7,['heavy1'+str(i) for i in range(7)]])
light = Process(target=loop,args=[500000,10,['light'+str(i) for i in range(10)]])
heavy2 = Process(target=loop,args=[1000000,3,['heavy2'+str(i) for i in range(7,10)]])
heavy1.start()
light.start()
light.join()
heavy2.start()
heavy1.join()
heavy2.join()
输出:

light0
heavy10
light1
light2
heavy11
light3
light4
heavy12
light5
light6
heavy13
light7
light8
heavy14
light9
heavy15
heavy27
heavy16
heavy28
heavy29
更好的利用。当然,您可以通过为慢进程运行共享一个队列来提高这一点,这样当快进程完成时,他们可以作为慢队列上的工作人员加入,但是对于仅两个不同的调用,这可能是过度的,尽管使用。最佳解决方案:

这是您想要的最佳且可扩展的解决方案。输出:

Worker 0 : light0
Worker 0 : light1
Worker 1 : heavy0
Worker 1 : light2
Worker 0 : heavy1
Worker 0 : light3
Worker 1 : heavy2
Worker 1 : light4
Worker 0 : heavy3
Worker 0 : light5
Worker 1 : heavy4
Worker 1 : light6
Worker 0 : heavy5
Worker 0 : light7
Worker 1 : heavy6
Worker 1 : light8
Worker 0 : heavy7
Worker 0 : light9
Worker 1 : heavy8
Worker 0 : heavy9


如果只想调用myFunc一次,为什么在for循环中对每个参数集调用myFunc 10次?@dnswlt对不起,我的错,我忘记了在每次迭代中参数都会更改。更新了我的问题现在你应该使用一个进程池,不管你想要多少,在开始时提交所有作业,让它们全部运行到完成。通过这种方式,您可以在整个运行过程中利用所有可用的内核,而当前两个并行作业中速度较快的将首先完成,然后该内核上将不会发生任何事情。@JohnZwinck谢谢您,但您能否尝试发布一个答案?我对多重处理相当陌生,不清楚如何正确地使用它。我认为您对需求的定义不够明确。1.您不希望并行化所有运行2。您不希望在最后一次尝试时一次只并行2个调用。那你想要什么?一次并行所有10个快速调用,然后依次运行慢速调用?在连续运行快速并行运行的同时运行慢速调用?在这种情况下,当慢运行结束时,是否要转到下一个慢运行,仍然并行快速运行?如果只想调用myFunc一次,为什么在for循环中对每个参数集调用myFunc 10次?@dnswlt对不起,我的错,我忘了提到每次迭代时参数都会更改。更新了我的问题现在你应该使用一个进程池,不管你想要多少,在开始时提交所有作业,让它们全部运行到完成。通过这种方式,您可以在整个运行过程中利用所有可用的内核,而当前两个并行作业中速度较快的将首先完成,然后该内核上将不会发生任何事情。@JohnZwinck谢谢您,但您能否尝试发布一个答案?我对多重处理相当陌生,不清楚如何正确地使用它。我认为您对需求的定义不够明确。1.您不希望并行化所有运行2。您不希望在最后一次尝试时一次只并行2个调用。那你想要什么?一次并行所有10个快速调用,然后依次运行慢速调用?在连续运行快速并行运行的同时运行慢速调用?在这种情况下,当慢跑结束时,您是否要继续下一次慢跑,仍然并行快速运行?非常好的答案!非常感谢。我认为队列解决方案更适合我的工作。@Amir,这是正确的解决方案。而且对于不同数量的工作和工人来说,工作会更好。当你只想给工人提供工作机会时,这是一种经典的工作模式。我刚刚意识到所有这些方法都存在一个问题。我发现我正在运行的函数是一个类myClass.myFunc中的函数。在类init self中,我定义了一些变量,想象它们像计数器。然后,我想并行化的内部函数更新这些变量,我想在myClass.myFunc调用之外使用它们。然而,由于进程以某种方式创建了自己的环境,所以我不能在类之外使用这些变量。很抱歉增加了问题的复杂性,但有解决方案吗?它不必使用多处理,但我不想使用线程,因为据我所知,线程不会使用所有可用的内核。@Amir您一直在为问题添加新的要求,这正在演变为聊天。你说的内部是什么意思?我建议-你解决了你的工作模式,现在你想问一下共享内存。写一个只涉及两个进程的新问题,以及您希望它们如何共享mem
因此,简化我们在这里解决的硬/轻流程问题-链接到这里,我来看看。非常好的答案!非常感谢。我认为队列解决方案更适合我的工作。@Amir,这是正确的解决方案。而且对于不同数量的工作和工人来说,工作会更好。当你只想给工人提供工作机会时,这是一种经典的工作模式。我刚刚意识到所有这些方法都存在一个问题。我发现我正在运行的函数是一个类myClass.myFunc中的函数。在类init self中,我定义了一些变量,想象它们像计数器。然后,我想并行化的内部函数更新这些变量,我想在myClass.myFunc调用之外使用它们。然而,由于进程以某种方式创建了自己的环境,所以我不能在类之外使用这些变量。很抱歉增加了问题的复杂性,但有解决方案吗?它不必使用多处理,但我不想使用线程,因为据我所知,线程不会使用所有可用的内核。@Amir您一直在为问题添加新的要求,这正在演变为聊天。你说的内部是什么意思?我建议-你解决了你的工作模式,现在你想问一下共享内存。写一个只涉及两个进程的新问题,以及您希望它们如何共享内存,因此请简化我们在这里解决的硬/轻进程问题-链接到这里,我来看看。