我的问题基本上归结为“如何让这部分线程化的Python代码在我的GPU而不是CPU上运行?”
我正在开发一个类似于Traveling Salesman问题的程序,在该程序中,我递归检查每个可能的移动(当然是进行了优化)。棘手的事情是保留全局变量-python的本机线程做得很好,而我使用的算法完全依赖于全局变量-总的来说,我知道。冒着过多细节的风险,我的许多线程将不得不生成自己的单独线程,直到达到某个“深度”为止(我发现大约3种效果最好),此时每个线程将不再进行并行化,该函数将改为线性执行。
起初它工作得很好,然后通过对其进行线程化,我获得了一些性能改进。但是,它仍然不够好-如果我可以维护全局变量,那么从理论上讲该程序可以完全并行化,因此我认为它可以在GPU上非常快速地运行。
此刻代码很杂乱,但这是用伪代码表达的一般思想:
int x
function f( depth ): # THE RECURSIVE f( n ) TEMPLATE
global x
# do stuff with x
if depth <= maxDepth then # if we're still below the max depth
# then we'll thread the next round of recursion.
for i = 0 to n # this number will change each time
call_in_thread( target = f,
args = depth + 1
) # obviously the arguments
# passed to each thread will be a little different,
# but that shouldn't be a problem
else # if we're already past the max depth,
# then we won't bother parallelising,
# as the overheads would outweigh the benefits
for i = 0 to n #
f( depth + 1 ) # THE SELF-RECURSIVE CALL
所以我的问题很简单-我可以(轻松地)从线程化的python程序转换为在我的GPU上运行的线程化的python程序,同时仍然保持对全局变量的使用吗?我知道Numba / NumbaPro的存在,但是它们非常令人生畏,但我不确定像我这样的程序将如何转化为该框架。
答案 0 :(得分:-1)
Q:我可以(轻松地)从线程化的python程序转换为在我的GPU上运行的线程化的python程序,同时仍然保持对全局变量的使用吗?
A:原则上两者都不是,越不容易:
a)上面的伪代码中正式绘制的递归有缺陷的。它不控制自己的终止,而是发散到无穷大,累积所有按次分配的资源,并等待递归的最深返回点,直到它开始释放任何此类等待资源分配,因为递归合并阶段会“冒泡”回到初始调用方。
b)(截至2019年第3季度)python 线程主要是零并发的,due to the GIL-lock prevented concurrency和python线程并发的处理在已知的情况下运行更差(由于到增加的新开销),然后是纯 [SERIAL]
流程(例外:可能存在延迟屏蔽(隐藏外部或与I / O相关的请求/响应延迟和传输延迟)与将请求,等待时间和后处理的纯[SERIAL]
排序相比,多线程慢任务分成多个线程的好处是,其中等待时间的并发是改进的源泉响应(最终到达(或未到达))
c)是泛型递归的一种形式,是一种纯[SERIAL]
嵌套的多层深度(有时非常深,但从未无限),并带有在递归的合并阶段,在终端值的“反向传播”过程中,一直推迟使用更深层次的尚未计算的值。尽管有特定的机会,但是使用递归形式进行自我表达的通用算法几乎无法并行化,efficiently parallelised ( see the role of all add-on costs in the Amdahl's Law )越少越好。这样做的附加间接费用。再加上递归调用过程中的计算“密度”下降,而资源需求却一直上升。在这方面,计算机科学的规则非常明确。观察并play with this fully interactive animated impact 分析,并尝试将 p
滑块移动到处理过程中[PARALLEL]
部分的global
部分而不是不现实的100%并行代码,好像具有零状态附加成本,好像具有零转移附加成本,好像具有零结果合并附加成本,至少“闻到现实生活中的烟雾”。
d) NUMA生态系统( N 在 U niform M emory A 强大的>访问系统,更多的是通过代理(设备驱动程序)介导的对外语的访问(无论是CUDA c语言,OpenCL,基于SoC / FPGA的混合语言还是其他任何语言,编译成静态GPU内核代码,转移到GPU卡的“内部”,并在那儿“自动”执行,而是在远程分布式硬件上托管,因为GPU是的一个示例)期望具有在呼叫发起子系统中固有的所有属性,并且在状态/值变化的双向协调传播中,该属性均匀可用并且可以正常工作。强烈建议不要在python端使用 {{1}} 变量(已知情况除外),即使在仅使用同种python的同类软件中也是如此
e)(Python解释器解释的程序)(Python解释器解释的程序),线程数量越少,则无法在GPU上运行。 GPU设备也有其代码执行单元,称为线程,但是它们主要在不同的硬件(基于供应商的基于SM的设备)上执行,这些硬件表现出SIMD并行性,因此是自主操作的线程,每个线程都有一个线程。要执行的代码不同,甚至具有相同的代码,但执行路径不同(GPU术语不同),SIMD设备将浪费时间安排SIMD执行线程,并让所有不连贯的SIMD线程等待它们(不同),直到块中没有其他SIMD线程具有完全相同的SIMD指令来执行,直到他们的指令(不同的每个线程)将获得全SIMD“空闲”才能执行。如果发散线程不得不在〜2 GHz SIMD设备上等待一段将来的时间,那么32个线程(宽)的SIMD设备可以完全摆脱其他任何工作,并且可以首先使用,因此人们可以猜测性能会大大下降。 “私下”提供一个(发散)SIMD线程。是的,有一些python工具可以使python程序准备另一个“嵌入式”代码块,然后对其进行转换,编译,然后通过GPU设备队列下载到结构中并计划用于GPU设备上的远程执行,但是所有这些转换都是复杂的,因此要使高级算法了解如何使用外部设备处理器的机器代码将此类算法转换到外部设备计算设备上在那里执行,同时还必须遵守所有已知的特定于外部设备的资源和处理组织策略约束。
f)实际上,GPU托管的内核代码对于资源的使用确实非常敏感,如果是递归的则更多。任何种类的递归算法的递归深度(如本文a所述,此处是相异的)可能甚至会自行导致正确公式化的递归代码无法直接在GPU设备内运行,这是由于固定且数量很少的高性能片上内存资源(SM高速缓存的大小非常有限,即使是在HPC驱动的GPU架构上,如果与当代CPU上的大小相比,也没有提及主流,主要是面向游戏的GPU卡)。