这个问题在进入多核时代以来,似乎多了一种解决途径,那就是充分利用多处理器(核)来并行的完成计算任务.
这里的多处理环境可以是指一台拥有多核处理器甚至多颗处理器的服务器,也可以是指一个拥有多节点的计算机群。
针对开篇提出的那个问题,解决办法其实都是"分而治之"。从数据分割入手,我们可以得到类似MapReduce的解决方案,类似的Python实现也有不少;从程序角度去看,目前主要是以下几种途径:
- 大规模并行处理系统(MPP,Figure 1)
- 对称多处理(SMP,Figure 2)
- 分布式计算(集群/网格计算,Figure 3)
Figure 1 MPP
Figure 2 SMP
Figure 3 Cluster
本文主要介绍的ParallelPython(简称pp)框架可以有效支持SMP和集群方式进行并行计算。
根据官方介绍,pp提供了在SMP(多CPU或多核)和集群(通过网络连接的多台计算机)上并行执行Python代码的机制,具有以下特性:
- 在SMP和集群上并行执行Python代码
- 易于理解和实现的基于工作的并行机制,便于把穿行应用转换成并行的
- 自动构造最佳配置(默认时工作进程数量等同于系统处理器数量)
- 动态处理器分配(允许运行时改变工作处理器数量)
- 函数的工作缓存(透明的缓存机制确保后续调用降低负载)
- 动态负载均衡(任务被动态的分配到各个处理器上)
- 基于SHA的连接加密认证
- 跨平台移植(Windows/Linux/Unix)
和传统的线程模型不同的是,thread和threading模块无法在字节码一级实现并行。因为Python解释器使用GIL(全局解释器锁)来在内部禁止并行执行。这个GIL限制你在SMP机器上同一时间也 只能执行一条字节码指令。而pp在内部使用进程和进程间通信来组织并行计算。并隐藏了所有内部的细节和复杂性,应用程序只需要提交工作任务并取回结果就可以了。
代码说话:
import pp
nodes = ('10.0.0.1',)
jober = pp.Server(ppservers=nodes)
f = jober.submit(func,args,depfunc,module)
nodes = ('10.0.0.1',)
jober = pp.Server(ppservers=nodes)
f = jober.submit(func,args,depfunc,module)
submit函数接受worker具体执行的函数func,参数args以及func内部调用的函数depfunc和涉及到的模块module
相应的节点机器上执行:
./ppservers.py
不过节点机器上可能会报错"Socket connection is broken".解决办法如下:
class Close(Exception):
pass
def send(self, data):
bufsz = self.bufsz
t_size = len(data)
size = struct.pack('!Q', t_size)
p_size = self.socket.send(size)
if p_size == 0:
raise Close('end connection')
s_size = 0L
while s_size < t_size:
nd_sz = min(bufsz, t_size - s_size)
p_size = self.socket.send(data[s_size:s_size+nd_sz])
if p_size == 0:
raise Close('end connection')
s_size += p_size
pass
def send(self, data):
bufsz = self.bufsz
t_size = len(data)
size = struct.pack('!Q', t_size)
p_size = self.socket.send(size)
if p_size == 0:
raise Close('end connection')
s_size = 0L
while s_size < t_size:
nd_sz = min(bufsz, t_size - s_size)
p_size = self.socket.send(data[s_size:s_size+nd_sz])
if p_size == 0:
raise Close('end connection')
s_size += p_size