DS *_*S R 5 python arrays parallel-processing function
一个简单的程序,用于计算数字平方并存储结果:
import time
from joblib import Parallel, delayed
import multiprocessing
array1 = [ 0 for i in range(100000) ]
def myfun(i):
return i**2
#### Simple loop ####
start_time = time.time()
for i in range(100000):
array1[i]=i**2
print( "Time for simple loop --- %s seconds ---" % ( time.time()
- start_time
)
)
#### Parallelized loop ####
start_time = time.time()
results = Parallel( n_jobs = -1,
verbose = 0,
backend = "threading"
)(
map( delayed( myfun ),
range( 100000 )
)
)
print( "Time for parallelized method --- %s seconds ---" % ( time.time()
- start_time
)
)
Run Code Online (Sandbox Code Playgroud)
#### Output ####
# >>> ( executing file "Test_vr20.py" )
# Time for simple loop --- 0.015599966049194336 seconds ---
# Time for parallelized method --- 7.763299942016602 seconds ---
Run Code Online (Sandbox Code Playgroud)
这两个选项的数组处理是否可能有所不同?我的实际程序会有一些更复杂的东西,但这是我需要尽可能并行化的一种计算,但不会得到这样的结果。
System Model: HP ProBook 640 G2, Windows 7,
IDLE for Python System Type: x64-based PC Processor:
Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz,
2401 MHz,
2 Core(s),
4 Logical Processor(s)
Run Code Online (Sandbox Code Playgroud)
我喜欢蟒蛇。
我祈祷教育工作者更好地解释工具的成本,否则我们会迷失在这些希望得到的[PARALLEL]时间表中。
No. 0:通过大量简化,Python 有意使用GIL来[SERIAL]访问变量,从而避免[CONCURRENT]修改造成的任何潜在冲突 - 在额外的时间内支付 GIL-stepped dance 的附加成本
No. 1:[PARALLEL]-代码执行比“只是”要困难得多- [CONCURRENT](阅读更多)
第二:如果试图将工作分配给工人,则进程必须支付额外成本第三:如果一个进程进行工人间通信,则每个进程必须支付巨大的额外成本第四:如果硬件用于处理的资源很少,结果会变得更糟[SERIAL][CONCURRENT][CONCURRENT]
效率在于更好地使用芯片,而不是将语法构造函数推入合法的领域,但它们的性能会对测试中的实验端到端速度产生不利影响:
您只需8 ~ 11 [ms]迭代地组装一个空的array1
>>> from zmq import Stopwatch
>>> aClk = Stopwatch()
>>> aClk.start();array1 = [ 0 for i in xrange( 100000 ) ];aClk.stop()
9751L
10146L
10625L
9942L
10346L
9359L
10473L
9171L
8328L
Run Code Online (Sandbox Code Playgroud)
(该Stopwatch().stop()方法[us]从得出.start())
,而内存高效、可向量化、无 GIL 的方法可以更快地完成相同的操作,速度大约 +230x ~ +450x:
>>> import numpy as np
>>>
>>> aClk.start();arrayNP = np.zeros( 100000 );aClk.stop()
15L
22L
21L
23L
19L
22L
>>> aClk.start();arrayNP = np.zeros( 100000, dtype = np.int );aClk.stop()
43L
47L
42L
44L
47L
Run Code Online (Sandbox Code Playgroud)
>>> def test_SERIAL_python( nLOOPs = 100000 ):
... aClk.start()
... for i in xrange( nLOOPs ): # py3 range() ~ xrange() in py27
... array1[i] = i**2 # your loop-code
... _ = aClk.stop()
... return _
Run Code Online (Sandbox Code Playgroud)
虽然简单的迭代实现有效,但选择对 100000 维向量[SERIAL]执行此操作会付出巨大的成本:~ 70 [ms]
>>> test_SERIAL_python( nLOOPs = 100000 )
70318L
69211L
77825L
70943L
74834L
73079L
Run Code Online (Sandbox Code Playgroud)
>>> aClk.start();arrayNP[:] = arrayNP[:]**2;aClk.stop()
189L
171L
173L
187L
183L
188L
193L
Run Code Online (Sandbox Code Playgroud)
还有另一个小故障,又名就地操作方式:
>>> aClk.start();arrayNP[:] *=arrayNP[:];aClk.stop()
138L
139L
136L
137L
136L
136L
137L
Run Code Online (Sandbox Code Playgroud)
性能的艺术不在于遵循听起来像营销的
并行化主张(不惜任何代价),
而在于使用基于专有技术的方法,以最少的成本获得最大的加速。
对于“小”问题,分发“薄”工作包的典型成本确实很难通过任何可能实现的加速来覆盖,因此“问题大小”实际上限制了人们对方法的选择,而这可以实现正收益(加速StackOverflow 上经常报道 0.9 甚至 << 1.0,因此您不必在这种惊喜中感到迷失或孤独)。
处理器数量很重要。
核心数量很重要。
但缓存大小 + NUMA 不规则性的影响远不止于此。
智能、矢量化、HPC 固化、无 GIL 库很重要
(numpy等人 - 非常感谢 Travis OLIPHANT 等人......向他的团队致敬......)
正如开销严格的阿姆达尔定律(重新)公式所解释的那样,为什么即使是许多NCPU并行代码执行也可能(而且确实经常)受到加速 << 1.0 的影响
阿姆达尔定律加速的开销严格制定包括支付的设置 +终止开销S的成本,明确:[PAR]-[PAR]-
1
S = __________________________; where s, ( 1 - s ), N were defined above
( 1 - s ) pSO:= [PAR]-Setup-Overhead add-on
s + pSO + _________ + pTO pTO:= [PAR]-Terminate-Overhead add-on
N
Run Code Online (Sandbox Code Playgroud)
(这里引用了一个交互式动画工具,用于对这些性能限制进行 2D 可视化效果)
来自以下文档threading:
如果您知道您正在调用的函数基于已编译的扩展,该扩展会在其大部分计算期间释放 Python 全局解释器锁 (GIL)...
问题是在这种情况下,你不知道这一点。Python 本身只允许一个线程同时运行(Python 解释器每次执行 Python 操作时都会锁定 GIL)。
threadingmyfun()仅当大部分时间都花在已编译的 Python 扩展上并且该扩展释放了 GIL时才会有用。
该Parallel代码速度慢得令人尴尬,因为您需要做大量工作来创建多个线程,然后您一次只能执行一个线程。
如果使用multiprocessing后端,则必须将输入数据复制到四个或八个进程中的每一个(每个核心一个),在每个进程中进行处理,然后将输出数据复制回来。复制会很慢,但如果处理比仅仅计算平方更复杂一点,那么可能是值得的。测量并查看。