我正在尝试在python下运行并行进程(在ubuntu上)。
我开始使用多处理,它适用于简单的例子 然后是泡菜错误,所以我切换到了悲..我对不同的选项感到困惑,所以写了一个非常简单的基准测试代码。
import multiprocessing as mp
from pathos.multiprocessing import Pool as Pool1
from pathos.pools import ParallelPool as Pool2
from pathos.parallel import ParallelPool as Pool3
import time
def square(x):
# calculate the square of the value of x
return x*x
if __name__ == '__main__':
dataset = range(0,10000)
start_time = time.time()
for d in dataset:
square(d)
print('test with no cores: %s seconds' %(time.time() - start_time))
nCores = 3
print('number of cores used: %s' %(nCores))
start_time = time.time()
p = mp.Pool(nCores)
p.map(square, dataset)
# Close
p.close()
p.join()
print('test with multiprocessing: %s seconds' %(time.time() - start_time))
start_time = time.time()
p = Pool1(nCores)
p.map(square, dataset)
# Close
p.close()
p.join()
print('test with pathos multiprocessing: %s seconds' %(time.time() - start_time))
start_time = time.time()
p = Pool2(nCores)
p.map(square, dataset)
# Close
p.close()
p.join()
print('test with pathos pools: %s seconds' %(time.time() - start_time))
start_time = time.time()
p = Pool3()
p.ncpus = nCores
p.map(square, dataset)
# Close
p.close()
p.join()
print('test with pathos parallel: %s seconds' %(time.time() - start_time))
我得到了
- 使用普通串行代码的0.001s,无并行,
- 带multiprocessing
选项的0.100s,
- 带pathos.multiprocessing
的0.100秒,
- 4.470s pathos.pools
,
- AssertionError
pathos.parallel
错误
我复制了如何使用http://trac.mystic.cacr.caltech.edu/project/pathos/browser/pathos/examples.html
中的各种选项据我所知,对于这样一个简单的例子,并行处理比普通的串行代码要长。我不明白的是悲伤的相对表现。
我检查了讨论,但无法理解为什么pathos.pools
会更长,以及为什么我会收到错误(不确定最后一个选项的性能是什么)。
我也尝试使用简单的方形函数,因此即使pathos.multiprocessing
比multiprocessing
有人可以解释这些不同选项之间的差异吗?
此外,我在运行centOS的远程计算机上运行pathos.multiprocessing
选项,性能比<{1}} <<>> 10倍。
根据租用计算机的公司,它应该像家用电脑一样工作。我知道,如果没有关于机器的更多细节,提供信息可能会很困难,但如果你对它的来源有任何想法,那将有所帮助。
答案 0 :(得分:8)
我是pathos
作者。对困惑感到抱歉。您正在处理新旧编程接口的混合。
“新”(建议)界面是使用pathos.pools
。旧的界面链接到相同的对象,所以它实际上有两种方法可以达到同样的目的。
multiprocess.Pool
是multiprocessing.Pool
的分支,唯一的区别是multiprocessing
使用pickle
而multiprocess
使用dill
。所以,我希望在大多数简单的情况下速度都是一样的。
上面的游泳池也可以在pathos.pools._ProcessPool
找到。 pathos
为几种类型的池提供了一个小包装器,具有不同的后端,提供了扩展功能。 pathos
- 包裹的池为pathos.pools.ProcessPool
(旧界面在pathos.multiprocessing.Pool
处提供)。
首选界面为pathos.pools.ProcessPool
。
还有ParallelPool
,它使用不同的后端 - 它使用ppft
而不是multiprocess
。 ppft
是“parallel python”,它通过subprocess
生成python进程并传递源代码(使用dill.source
而不是序列化对象) - 它用于分布式计算,或者当通过源代码传递时是一个更好的选择。
因此,pathos.pools.ParallelPool
是首选界面,pathos.parallel.ParallelPool
(和pathos
中的一些其他类似引用)因遗留原因而闲置 - 但它们是同一个对象
总结:
>>> import multiprocessing as mp
>>> mp.Pool()
<multiprocessing.pool.Pool object at 0x10fa6b6d0>
>>> import multiprocess as mp
>>> mp.Pool()
<multiprocess.pool.Pool object at 0x11000c910>
>>> import pathos as pa
>>> pa.pools._ProcessPool()
<multiprocess.pool.Pool object at 0x11008b0d0>
>>> pa.multiprocessing.Pool()
<multiprocess.pool.Pool object at 0x11008bb10>
>>> pa.pools.ProcessPool()
<pool ProcessPool(ncpus=4)>
>>> pa.pools.ParallelPool()
<pool ParallelPool(ncpus=*, servers=None)>
您可以看到ParallelPool
已servers
...因此适用于分布式计算。
唯一剩下的问题是为什么AssertionError
?那是因为pathos
添加的包装器使池对象可以重用。因此,当您再次呼叫ParallelPool
时,您正在呼叫一个封闭的池。您需要restart
池才能再次使用它。
>>> f = lambda x:x
>>> p = pa.pools.ParallelPool()
>>> p.map(f, [1,2,3])
[1, 2, 3]
>>> p.close()
>>> p.join()
>>> p.restart() # throws AssertionError w/o this
>>> p.map(f, [1,2,3])
[1, 2, 3]
>>> p.close()
>>> p.join()
>>> p.clear() # destroy the saved pool
关于重新启动和清除已保存的实例,ProcessPool
与ParallelPool
具有相同的界面。
答案 1 :(得分:2)
让我们从一些共同点开始。
Python解释器使用GIL步进式代码执行作为标准。这意味着,所有基于线程的池仍然等待所有代码执行路径的GIL步骤排序,因此任何这样构造的尝试将不会享受好处“理论上预期”。
Python解释器可以使用其他基于进程的实例来加载多个进程,每个进程都有自己的GIL锁,形成多个并发代码执行路径池。
在管理了这种主要的歧视之后,与性能相关的问题开始出现了。最负责任的方法是基准,基准,基准。这里也不例外。
主要(常量)部分是流程实例化的主要 [TIME]
- 域成本。在这里,python解释器的完整副本,包括所有变量,所有内存映射,实际上是调用python解释器的完整状态全副本必须首先创建并放置到操作系统进程调度程序表中,然后再进一步(作业的有用部分)计算“内部”这样成功实例化的子过程可以发生。如果您的有效负载功能刚刚从那里返回,创建了 x*x
,您的代码似乎已经为一些CPU指令烧掉了所有的燃料,并且您花费的时间超过了返回。 成本经济不利于您,因为所有流程实例化和流程终止成本都高于几个CPU-CLOCK滴答。
实际需要多长时间?
您可以在建议的 Test-Case-A
中对此进行基准测试(建议 here 。如果Stopwatch()
- ed {{1决定,你开始依赖事实而不是任何种类的崇拜者或营销类型的建议。这是公平的,不是吗?)。
[us]
基准测试流程实例化成本[已测量]。下一个最危险的(变量大小)部分主要是 Test-Case-A
- 域成本,但还有[SPACE]
- 域影响,如果[TIME]
- 分配成本开始增长超过小占地面积。
这种附加开销成本与任何需要传递“大”参数,从“main”-python解释器到每个(分布式)子流程实例都有关。
这需要多长时间?
同样,基准,基准,基准。应该对此进行基准测试(建议 here ,如果扩展了 [SPACE]
,并替换 Test-Case-C
参数,其中包含一些确实“胖”的数据块,无论是 aNeverConsumedPAR
还是其他类型,都会带来巨大的内存占用。)
这样,真实的硬件相关+ O / S相关+ python相关的数据流成本开始变得可见,并在numpy.ndarray()
中的额外开销成本等基准测量中进行测量。这对于黑客来说并不是什么新鲜事,然而,那些从未遇到过HDD磁盘写入时间的人可能会成长并阻止其他处理很多秒或几分钟很难相信,如果没有触及一个人自己对数据流的实际成本进行基准测试。所以,不要犹豫将基准**[us]**
扩展到确实存在的大量记忆足迹,以闻到烟雾......
根据计算部分以及根据所有开销部分,可以很好地理解并行某些计算的尝试,图片开始变得完整:
overhead-strict and resources-aware Amdahl's Law re-formulation显示:
Test-Case-C
由此产生的加速 1
S = ______________________________________________ ; where s,
/ \ ( 1 - s ),
| ( 1 - s ) | pSO,
s + pSO + max| _________ , atomicP | + pTO pTO,
| N | N
\ / have been defined in
just an Overhead-strict Law
and
atomicP := is a further indivisible duration of an atomic-process-block
总是会因高昂的管理费用 S
而遭遇由于 pSO + pTO
的值足够高,因此不允许高 N
进一步提供帮助。
在所有这些情况下,最终加速atomicP
可能很容易属于S
,是的,完全属于纯 { {1}} 代码执行路径计划(同样,对 << 1.0
和 [SERIAL]
的实际成本进行了基准测试(对此示意性地提出了Test-Case-A + Test-Case-C(扩展))有机会得出所需的最小合理计算有效载荷,以便保持在加速的神秘水平之上的 pSO
强>