gpt4 book ai didi

python - 为什么 joblib 并行执行会使运行时慢很多?

转载 作者:行者123 更新时间:2023-12-05 06:08:46 26 4
gpt4 key购买 nike

我想打乱 3D numpy 数组中的值,但前提是它们 > 0。

当我用单核运行我的函数时,它甚至比使用 2 个核快得多。这远远超出了创建新 python 进程的开销。我错过了什么?

以下代码输出:

random shuffling of markers started
time in serial execution: 1.0288s
time executing in parallel with num_cores=1: 0.9056s
time executing in parallel with num_cores=2: 273.5253s
import numpy as np
import time
from random import shuffle
from joblib import Parallel, delayed
import multiprocessing

import numpy as np

def randomizeVoxels(V,markerLUT):
V_rand=V.copy()
# the xyz naming here does not match outer convention, which will depend on permutation
for ix in range(V.shape[0]):
for iy in range(V.shape[1]):
if V[ix,iy]>0:
V_rand[ix,iy]=markerLUT[V[ix,iy]]

return V_rand

V_ori=np.arange(1000000,-1000000,-1).reshape(100,100,200)

V_rand=V_ori.copy()

listMarkers=np.unique(V_ori)
listMarkers=[val for val in listMarkers if val>0]

print("random shuffling of markers started\n")

reassignedMarkers=listMarkers.copy()
#random shuffling of original markers
shuffle(reassignedMarkers)

markerLUT={}
for i,iMark in enumerate(listMarkers):
markerLUT[iMark]=reassignedMarkers[i]

tic=time.perf_counter()

for ix in range(len(V_ori)):
for iy in range(len(V_ori[0])):
for iz in range(len(V_ori[0][0])):
if V_ori[ix,iy,iz]>0:
V_rand[ix,iy,iz]=markerLUT[V_ori[ix,iy,iz]]

toc=time.perf_counter()

print("time in serial execution: \t\t\t{: >4.4f} s".format(toc-tic))

#######################################################################3

num_cores = 1

V_rand=V_ori.copy()

tic=time.perf_counter()

results= Parallel(n_jobs=num_cores)\
(delayed(randomizeVoxels)\
(V_ori[imSlice,:,:],
markerLUT
)for imSlice in range(V_ori.shape[0]))

for i,resTuple in enumerate(results):
V_rand[i,:,:]=resTuple

toc=time.perf_counter()

print("time executing in parallel with num_cores={}:\t{: >4.4f} s".format(num_cores,toc-tic))

num_cores = 2

V_rand=V_ori.copy()

MASK = "time executing in parallel with num_cores={}:\t {: >4.4f}s"

tic=time.perf_counter() #----------------------------- [PERF-me]

results= Parallel(n_jobs=num_cores)\
(delayed(randomizeVoxels)\
(V_ori[imSlice,:,:],
markerLUT
)for imSlice in range(V_ori.shape[0]))

for i,resTuple in enumerate(results):
V_rand[i,:,:]=resTuple

toc=time.perf_counter() #----------------------------- [PERF-me]

print( MASK.format(num_cores,toc-tic) )

最佳答案

Q : "What am I missing?"

很可能是内存 I/O 瓶颈。

enter image description here

虽然处理的 numpy 部分在这里似乎很浅(shuffle 不计算一点,但在一对位置之间移动数据,不是吗?),对于大多数时候,这将不允许“足够的时间”(通过做任何有用的工作)来让内存 I/O 被重新排序的 CPU 核心指令屏蔽(引用 latency-costs 直接 +当代超标量 CISC 架构最低级别的跨 QPI 内存 I/O 操作具有高度推测性分支预测(对内存 I/O 绑定(bind)非分支紧密制作的部分没有用)和多核和多核核心 NUMA 设计)。

这很可能就是为什么即使是第一次分拆 concurrent process (无论是否强制驻扎在相同的 (这里是通过两步舞蹈过程的交错对共享 CPU 核心时间,同样是内存 I/O 限制,更糟糕的机会共享内存 I/O channel 上的延迟屏蔽...) 或任何其他 (如果必须执行非本地内存,此处会添加跨 QPI 附加延迟成本-I/O,内存 I/O 延迟屏蔽的机会再次恶化) CPU 核心。

CPU 内核跳跃,由 CPU 时钟加速策略的冲突效应强制执行(稍后开始违反热管理,因此将进程跳到下一个更冷的 CPU 内核上)将使所有 CPU 内核无效缓存的好处,因为没有预缓存的数据在下一个更冷的核心可用,因此必须再次重新获取所有(一旦预缓存已经进入最快的 L1 数据缓存)数据(也许,对于具有更大的数组对象内存占用,甚至需要跨 QPI 获取),因此利用更多内核不会对最终效率产生微不足道的影响。

enter image description here

;o)
numpy 高性能和智能处理在这里不是应该受到指责的 - 恰恰相反 - 它清楚地消除了 CPU“饥饿”状态 - 多年来被认为是我们所有现代 CPU 的性能天花板 - 这就是为什么我们看到如此多的核心 CPU,它们试图通过拥有越来越多的核心来规避这一瓶颈 -请参阅上面引用的评论硅级分析。

最后但并非最不重要
代码原样包含大量改进其性能的机会,numpy- smart-vectorised 是第一个提到的,避免了 range() 循环,所以还有更多的技巧可以遵循,所有这些最终都会让 headbang 陷入同样的​​麻烦——CPU 饥饿天花板

关于python - 为什么 joblib 并行执行会使运行时慢很多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65026499/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com