使用 OpenCL 优化而不是多处理池映射
Using OpenCL optimization instead of Multiprocessing pool map
在我的代码的第一个版本中,我使用了 python 的多处理库,应用于 16 个线程的主函数 MAIN_LOOP
,如下所示:
def MAIN_LOOP(lll, seed=None):
global aa
global bb
aa, bb = 0,0
if paramo == 0:
C_ij_GG, C_ij_LL, C_ij_GL = np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange)))
C_ij_GG_up, C_ij_LL_up, C_ij_GL_up = np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange)))
C_ij_GG_dw, C_ij_LL_dw, C_ij_GL_dw = np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange)))
while aa < len(zrange):
while bb < len(zrange):
if paramo == 0:
C_ij_GG[aa][bb], C_ij_LL[aa][bb], C_ij_GL[aa][bb] = Pobs_C(zpm, zrange[aa], zrange[bb], h[2], Omega_m[2], Omega_DE[2], w0[2], wa[2], C_IA, A_IA[2], n_IA[2], B_IA[2], E_tab, R_tab, DG_tab, DG_tab, WG_tab, W_tab, WIA_tab, l[lll], P_dd_C, R_tab(z_pk))
C_ij_GG_up[aa][bb], C_ij_LL_up[aa][bb], C_ij_GL_up[aa][bb] = Pobs_C(zpm, zrange[aa], zrange[bb], h[0], Omega_m[0], Omega_DE[0], w0[0], wa[0], C_IA, A_IA[0], n_IA[0], B_IA[0], E_tab_up, R_tab_up, DG_tab, DG_tab_up, WG_tab_up, W_tab_up, WIA_tab_up, l[lll], P_dd_C_up, R_tab_up(z_pk))
C_ij_GG_dw[aa][bb], C_ij_LL_dw[aa][bb], C_ij_GL_dw[aa][bb] = Pobs_C(zpm, zrange[aa], zrange[bb], h[3], Omega_m[3], Omega_DE[3], w0[3], wa[3], C_IA, A_IA[3], n_IA[3], B_IA[3], E_tab_dw, R_tab_dw, DG_tab, DG_tab_dw, WG_tab_dw, W_tab_dw, WIA_tab_dw, l[lll], P_dd_C_dw, R_tab_dw(z_pk))
bb=bb+1
bb=0
aa=aa+1
if paramo == 0:
aa, bb = 0,0
outGG=open(pre_CC_path[0]+CC_path[2]+"/COVAR_fid_"+str(l[lll]),'w')
outLL=open(pre_CC_path[1]+CC_path[2]+"/COVAR_fid_"+str(l[lll]),'w')
outGL=open(pre_CC_path[2]+CC_path[2]+"/COVAR_fid_"+str(l[lll]),'w')
while aa < len(C_ij_GG):
while bb < len(C_ij_GG):
outGG.write(str("%.16e" % C_ij_GG[aa][bb]))
outGG.write(str(' '))
outLL.write(str("%.16e" % C_ij_LL[aa][bb]))
outLL.write(str(' '))
outGL.write(str("%.16e" % C_ij_GL[aa][bb]))
outGL.write(str(' '))
bb=bb+1
outGG.write(str('\n'))
outLL.write(str('\n'))
outGL.write(str('\n'))
bb=0
aa=aa+1
outGG.close()
outLL.close()
outGL.close()
aa, bb = 0,0
outGGU=open(pre_CC_path[0]+CC_path[0]+"/COVAR_up_"+str(l[lll]),'w')
outGGD=open(pre_CC_path[0]+CC_path[3]+"/COVAR_dw_"+str(l[lll]),'w')
outLLU=open(pre_CC_path[1]+CC_path[0]+"/COVAR_up_"+str(l[lll]),'w')
outLLD=open(pre_CC_path[1]+CC_path[3]+"/COVAR_dw_"+str(l[lll]),'w')
outGLU=open(pre_CC_path[2]+CC_path[0]+"/COVAR_up_"+str(l[lll]),'w')
outGLD=open(pre_CC_path[2]+CC_path[3]+"/COVAR_dw_"+str(l[lll]),'w')
while aa < len(C_ij_GG_up):
while bb < len(C_ij_GG_up):
outGGU.write(str("%.16e" % C_ij_GG_up[aa][bb]))
outGGU.write(str(' '))
outGGD.write(str("%.16e" % C_ij_GG_dw[aa][bb]))
outGGD.write(str(' '))
outLLU.write(str("%.16e" % C_ij_LL_up[aa][bb]))
outLLU.write(str(' '))
outLLD.write(str("%.16e" % C_ij_LL_dw[aa][bb]))
outLLD.write(str(' '))
outGLU.write(str("%.16e" % C_ij_GL_up[aa][bb]))
outGLU.write(str(' '))
outGLD.write(str("%.16e" % C_ij_GL_dw[aa][bb]))
outGLD.write(str(' '))
bb=bb+1
outGGU.write(str('\n'))
outGGD.write(str('\n'))
outLLU.write(str('\n'))
outLLD.write(str('\n'))
outGLU.write(str('\n'))
outGLD.write(str('\n'))
bb=0
aa=aa+1
outGGU.close()
outGGD.close()
outLLU.close()
outLLD.close()
outGLU.close()
outGLD.close()
lll=lll+1
lll = range(len(l))
if __name__ == '__main__':
pool = mp.Pool(16)
pool.map(MAIN_LOOP, lll)
并行版本位于末尾,即:
if __name__ == '__main__':
pool = mp.Pool(16)
pool.map(MAIN_LOOP, lll)
现在,我正在尝试使用另一种优化方法,我尝试通过 GPU/OpenCL 来做到这一点:
所以,我用 :
代替了这个并行化的 multiprocessing pool
代码部分
# NEW VERSION : with OpenCL
if __name__ == '__main__':
# GPU/OPenCL VERSION
# Select a device
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
# Kernel
prg = cl.Program(ctx, """
typedef int T;
// Extern MAIN_LOOP function
void MAIN_LOOP(__global T* in);
__kernel
void
gpu_map(__global T* in,
const size_t n)
{
unsigned gid = get_global_id(0);
// Call MAIN_LOOP with global_id
MAIN_LOOP(in[gid]);
}
""").build()
# Output compiler
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'
# Allocate memory on the device and copy the content of our numpy array
mf = cl.mem_flags
# Get kernel function
lll_np = np.array(lll, dtype=np.uint32)
# Create input numpy
lll_g = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=lll_np)
#Get kernel function
my_knl= prg.gpu_map
my_knl.set_scalar_arg_dtypes([None, np.int32])
my_knl(queue, lll_np.shape, None, lll_g, len(lll_np))
不幸的是,无法构建内核程序,出现以下错误:
Traceback (most recent call last):X2_non_flat_GPU_dev.py
File "X2_non_flat_GPU_OpenCL_dev.py", line 671, in <module>
""").build()
File "/Users/fab/Library/Python/2.7/lib/python/site-packages/pyopencl/__init__.py", line 510, in build
options_bytes=options_bytes, source=self._source)
File "/Users/fab/Library/Python/2.7/lib/python/site-packages/pyopencl/__init__.py", line 554, in _build_and_catch_errors
raise err
pyopencl._cl.RuntimeError: clBuildProgram failed: BUILD_PROGRAM_FAILURE - clBuildProgram failed: BUILD_PROGRAM_FAILURE - clBuildProgram failed: BUILD_PROGRAM_FAILURE
Build on <pyopencl.Device 'AMD Radeon Pro Vega 20 Compute Engine' on 'Apple' at 0x1021d00>:
Error returned by cvms_element_build_from_source
(options: -I /Users/fab/Library/Python/2.7/lib/python/site-packages/pyopencl/cl)
(source saved as /var/folders/y7/5dtgdjld5fxd3c1qm9hknlm40000gn/T/tmpg3pfTx.cl)
如何解决这些错误?
更新 1
关于我的代码基准测试的类似赏金已开始:
从运行时的角度来看,有更多关于贪婪代码部分的信息。但是这个赏金更多的是关于可以找到优化方法的一般想法。
在 OpenCL 中,您有一个主机(在您的例子中,您的 CPU 执行 Python 代码)和多个设备(通常是一个 GPU)。
设备要执行的主机入队操作:
- 创建缓冲区
- 将数据保存到缓冲区
- 执行内核
- 从缓冲区中检索结果
- 一些同步机制(障碍...)
[此列表并不详尽]
考虑一下您的 CPU 只有几个处理器,在当前的台式计算机中通常有 8-32 个。但是您的 GPU 有很多处理器(例如,在我的例子中是 4096 个内核)。
这个巨大的差异(32 到 4K)意味着 GPU 对于某些任务要快得多,但也意味着 CPU 线程和 GPU 之间不能有任何一对一的同步-线。我的意思是,CPU 可以在 GPU 上启动许多线程并检索结果,但是 GPU 线程不能调用 CPU 线程(因为你会有 128 个 GPU 核心试图调用同一个核心CPU,这会非常慢吧?)。
总结:如果MAIN_LOOP
是主机上的一个函数,你不能从内核中调用它。这对于 OpenCL、CUDA、OpenGL 或任何其他类似技术是不可能的(并且可能会保持这种状态很多年)。
然而,您可以做的是:
- 调用您的内核并将
MAIN_LOOP
的索引保存在缓冲区中
- 在Python中,等待内核完成,对检索到的每个索引执行
MAIN_LOOP
。
但是,如果您尝试从 OpenCL 内核调用 Python 代码,那么除了将 MAIN_LOOP
转换为 OpenCL 内核代码外,恐怕您别无选择。
即使您的主机程序是使用 PyOpenCL 在 Python 中编写的,如果您想使用 [=13= 编写在 GPU 上运行的代码,您的 OpenCL 内核也必须在 OpenCL C or OpenCL C++. There is no way around that. Have a look at Numba 中编写].
在我的代码的第一个版本中,我使用了 python 的多处理库,应用于 16 个线程的主函数 MAIN_LOOP
,如下所示:
def MAIN_LOOP(lll, seed=None):
global aa
global bb
aa, bb = 0,0
if paramo == 0:
C_ij_GG, C_ij_LL, C_ij_GL = np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange)))
C_ij_GG_up, C_ij_LL_up, C_ij_GL_up = np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange)))
C_ij_GG_dw, C_ij_LL_dw, C_ij_GL_dw = np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange))), np.zeros((len(zrange), len(zrange)))
while aa < len(zrange):
while bb < len(zrange):
if paramo == 0:
C_ij_GG[aa][bb], C_ij_LL[aa][bb], C_ij_GL[aa][bb] = Pobs_C(zpm, zrange[aa], zrange[bb], h[2], Omega_m[2], Omega_DE[2], w0[2], wa[2], C_IA, A_IA[2], n_IA[2], B_IA[2], E_tab, R_tab, DG_tab, DG_tab, WG_tab, W_tab, WIA_tab, l[lll], P_dd_C, R_tab(z_pk))
C_ij_GG_up[aa][bb], C_ij_LL_up[aa][bb], C_ij_GL_up[aa][bb] = Pobs_C(zpm, zrange[aa], zrange[bb], h[0], Omega_m[0], Omega_DE[0], w0[0], wa[0], C_IA, A_IA[0], n_IA[0], B_IA[0], E_tab_up, R_tab_up, DG_tab, DG_tab_up, WG_tab_up, W_tab_up, WIA_tab_up, l[lll], P_dd_C_up, R_tab_up(z_pk))
C_ij_GG_dw[aa][bb], C_ij_LL_dw[aa][bb], C_ij_GL_dw[aa][bb] = Pobs_C(zpm, zrange[aa], zrange[bb], h[3], Omega_m[3], Omega_DE[3], w0[3], wa[3], C_IA, A_IA[3], n_IA[3], B_IA[3], E_tab_dw, R_tab_dw, DG_tab, DG_tab_dw, WG_tab_dw, W_tab_dw, WIA_tab_dw, l[lll], P_dd_C_dw, R_tab_dw(z_pk))
bb=bb+1
bb=0
aa=aa+1
if paramo == 0:
aa, bb = 0,0
outGG=open(pre_CC_path[0]+CC_path[2]+"/COVAR_fid_"+str(l[lll]),'w')
outLL=open(pre_CC_path[1]+CC_path[2]+"/COVAR_fid_"+str(l[lll]),'w')
outGL=open(pre_CC_path[2]+CC_path[2]+"/COVAR_fid_"+str(l[lll]),'w')
while aa < len(C_ij_GG):
while bb < len(C_ij_GG):
outGG.write(str("%.16e" % C_ij_GG[aa][bb]))
outGG.write(str(' '))
outLL.write(str("%.16e" % C_ij_LL[aa][bb]))
outLL.write(str(' '))
outGL.write(str("%.16e" % C_ij_GL[aa][bb]))
outGL.write(str(' '))
bb=bb+1
outGG.write(str('\n'))
outLL.write(str('\n'))
outGL.write(str('\n'))
bb=0
aa=aa+1
outGG.close()
outLL.close()
outGL.close()
aa, bb = 0,0
outGGU=open(pre_CC_path[0]+CC_path[0]+"/COVAR_up_"+str(l[lll]),'w')
outGGD=open(pre_CC_path[0]+CC_path[3]+"/COVAR_dw_"+str(l[lll]),'w')
outLLU=open(pre_CC_path[1]+CC_path[0]+"/COVAR_up_"+str(l[lll]),'w')
outLLD=open(pre_CC_path[1]+CC_path[3]+"/COVAR_dw_"+str(l[lll]),'w')
outGLU=open(pre_CC_path[2]+CC_path[0]+"/COVAR_up_"+str(l[lll]),'w')
outGLD=open(pre_CC_path[2]+CC_path[3]+"/COVAR_dw_"+str(l[lll]),'w')
while aa < len(C_ij_GG_up):
while bb < len(C_ij_GG_up):
outGGU.write(str("%.16e" % C_ij_GG_up[aa][bb]))
outGGU.write(str(' '))
outGGD.write(str("%.16e" % C_ij_GG_dw[aa][bb]))
outGGD.write(str(' '))
outLLU.write(str("%.16e" % C_ij_LL_up[aa][bb]))
outLLU.write(str(' '))
outLLD.write(str("%.16e" % C_ij_LL_dw[aa][bb]))
outLLD.write(str(' '))
outGLU.write(str("%.16e" % C_ij_GL_up[aa][bb]))
outGLU.write(str(' '))
outGLD.write(str("%.16e" % C_ij_GL_dw[aa][bb]))
outGLD.write(str(' '))
bb=bb+1
outGGU.write(str('\n'))
outGGD.write(str('\n'))
outLLU.write(str('\n'))
outLLD.write(str('\n'))
outGLU.write(str('\n'))
outGLD.write(str('\n'))
bb=0
aa=aa+1
outGGU.close()
outGGD.close()
outLLU.close()
outLLD.close()
outGLU.close()
outGLD.close()
lll=lll+1
lll = range(len(l))
if __name__ == '__main__':
pool = mp.Pool(16)
pool.map(MAIN_LOOP, lll)
并行版本位于末尾,即:
if __name__ == '__main__':
pool = mp.Pool(16)
pool.map(MAIN_LOOP, lll)
现在,我正在尝试使用另一种优化方法,我尝试通过 GPU/OpenCL 来做到这一点:
所以,我用 :
代替了这个并行化的 multiprocessing pool
代码部分
# NEW VERSION : with OpenCL
if __name__ == '__main__':
# GPU/OPenCL VERSION
# Select a device
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
# Kernel
prg = cl.Program(ctx, """
typedef int T;
// Extern MAIN_LOOP function
void MAIN_LOOP(__global T* in);
__kernel
void
gpu_map(__global T* in,
const size_t n)
{
unsigned gid = get_global_id(0);
// Call MAIN_LOOP with global_id
MAIN_LOOP(in[gid]);
}
""").build()
# Output compiler
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'
# Allocate memory on the device and copy the content of our numpy array
mf = cl.mem_flags
# Get kernel function
lll_np = np.array(lll, dtype=np.uint32)
# Create input numpy
lll_g = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=lll_np)
#Get kernel function
my_knl= prg.gpu_map
my_knl.set_scalar_arg_dtypes([None, np.int32])
my_knl(queue, lll_np.shape, None, lll_g, len(lll_np))
不幸的是,无法构建内核程序,出现以下错误:
Traceback (most recent call last):X2_non_flat_GPU_dev.py
File "X2_non_flat_GPU_OpenCL_dev.py", line 671, in <module>
""").build()
File "/Users/fab/Library/Python/2.7/lib/python/site-packages/pyopencl/__init__.py", line 510, in build
options_bytes=options_bytes, source=self._source)
File "/Users/fab/Library/Python/2.7/lib/python/site-packages/pyopencl/__init__.py", line 554, in _build_and_catch_errors
raise err
pyopencl._cl.RuntimeError: clBuildProgram failed: BUILD_PROGRAM_FAILURE - clBuildProgram failed: BUILD_PROGRAM_FAILURE - clBuildProgram failed: BUILD_PROGRAM_FAILURE
Build on <pyopencl.Device 'AMD Radeon Pro Vega 20 Compute Engine' on 'Apple' at 0x1021d00>:
Error returned by cvms_element_build_from_source
(options: -I /Users/fab/Library/Python/2.7/lib/python/site-packages/pyopencl/cl)
(source saved as /var/folders/y7/5dtgdjld5fxd3c1qm9hknlm40000gn/T/tmpg3pfTx.cl)
如何解决这些错误?
更新 1
关于我的代码基准测试的类似赏金已开始:
从运行时的角度来看,有更多关于贪婪代码部分的信息。但是这个赏金更多的是关于可以找到优化方法的一般想法。
在 OpenCL 中,您有一个主机(在您的例子中,您的 CPU 执行 Python 代码)和多个设备(通常是一个 GPU)。
设备要执行的主机入队操作:
- 创建缓冲区
- 将数据保存到缓冲区
- 执行内核
- 从缓冲区中检索结果
- 一些同步机制(障碍...) [此列表并不详尽]
考虑一下您的 CPU 只有几个处理器,在当前的台式计算机中通常有 8-32 个。但是您的 GPU 有很多处理器(例如,在我的例子中是 4096 个内核)。
这个巨大的差异(32 到 4K)意味着 GPU 对于某些任务要快得多,但也意味着 CPU 线程和 GPU 之间不能有任何一对一的同步-线。我的意思是,CPU 可以在 GPU 上启动许多线程并检索结果,但是 GPU 线程不能调用 CPU 线程(因为你会有 128 个 GPU 核心试图调用同一个核心CPU,这会非常慢吧?)。
总结:如果MAIN_LOOP
是主机上的一个函数,你不能从内核中调用它。这对于 OpenCL、CUDA、OpenGL 或任何其他类似技术是不可能的(并且可能会保持这种状态很多年)。
然而,您可以做的是:
- 调用您的内核并将
MAIN_LOOP
的索引保存在缓冲区中 - 在Python中,等待内核完成,对检索到的每个索引执行
MAIN_LOOP
。
但是,如果您尝试从 OpenCL 内核调用 Python 代码,那么除了将 MAIN_LOOP
转换为 OpenCL 内核代码外,恐怕您别无选择。
即使您的主机程序是使用 PyOpenCL 在 Python 中编写的,如果您想使用 [=13= 编写在 GPU 上运行的代码,您的 OpenCL 内核也必须在 OpenCL C or OpenCL C++. There is no way around that. Have a look at Numba 中编写].