繁体   English   中英

带有pythons多处理功能的“ bucketsort”

[英]“bucketsort” with pythons multiprocessing

我有一个分布均匀的数据系列。 我希望利用发行版对数据进行并行排序。 对于N个CPU,我本质上定义了N个存储桶,并对并行存储桶进行排序。 我的问题是,我没有加快速度。

怎么了?

from multiprocessing import Process, Queue
from numpy import array, linspace, arange, where, cumsum, zeros
from numpy.random import rand
from time import time


def my_sort(x,y):
    y.put(x.get().argsort())

def my_par_sort(X,np):
 p_list=[]
 Xq = Queue()
 Yq = Queue()
 bmin = linspace(X.min(),X.max(),np+1) #bucket lower bounds
 bmax = array(bmin); bmax[-1] = X.max()+1 #bucket upper bounds
 B = []
 Bsz = [0]
 for i in range(np):
  b = array([bmin[i] <= X, X < bmax[i+1]]).all(0)
  B.append(where(b)[0])
  Bsz.append(len(B[-1]))
  Xq.put(X[b])
  p = Process(target=my_sort, args=(Xq,Yq))
  p.start()
  p_list.append(p)

 Bsz = cumsum(Bsz).tolist()
 Y = zeros(len(X)) 
 for i in range(np):
   Y[arange(Bsz[i],Bsz[i+1])] = B[i][Yq.get()]
   p_list[i].join()

 return Y


if __name__ == '__main__':
 num_el = 1e7
 mydata = rand(num_el)
 np = 4 #multiprocessing.cpu_count()
 starttime = time()
 I = my_par_sort(mydata,np)
 print "Sorting %0.0e keys took %0.1fs using %0.0f processes" % (len(mydata),time()-starttime,np)
 starttime = time()
 I2 = mydata.argsort()
 print "in serial it takes %0.1fs" % (time()-starttime)
 print (I==I2).all()

看起来您的问题是当将原始数组分解成碎片时要添加的开销量。 我拿走了您的代码,并删除了multiprocessing所有用法:

def my_sort(x,y): 
    pass
    #y.put(x.get().argsort())

def my_par_sort(X,np, starttime):
    p_list=[]
    Xq = Queue()
    Yq = Queue()
    bmin = linspace(X.min(),X.max(),np+1) #bucket lower bounds
    bmax = array(bmin); bmax[-1] = X.max()+1 #bucket upper bounds
    B = []
    Bsz = [0] 
    for i in range(np):
        b = array([bmin[i] <= X, X < bmax[i+1]]).all(0)
        B.append(where(b)[0])
        Bsz.append(len(B[-1]))
        Xq.put(X[b])
        p = Process(target=my_sort, args=(Xq,Yq, i)) 
        p.start()
        p_list.append(p)
    return

if __name__ == '__main__':
    num_el = 1e7 
    mydata = rand(num_el)
    np = 4 #multiprocessing.cpu_count()
    starttime = time()
    I = my_par_sort(mydata,np, starttime)
    print "Sorting %0.0e keys took %0.1fs using %0.0f processes" % (len(mydata),time()-starttime,np)
    starttime = time()
    I2 = mydata.argsort()
    print "in serial it takes %0.1fs" % (time()-starttime)
    #print (I==I2).all()

在绝对没有排序的情况下, multiprocessing代码所花费的时间与串行代码一样长:

Sorting 1e+07 keys took 2.2s using 4 processes
in serial it takes 2.2s

您可能会认为启动进程以及在进程之间传递值的开销是开销的原因,但是如果我删除所有对multiprocessing使用(包括Xq.put(X[b])调用),最终结果可能只是快点:

Sorting 1e+07 keys took 1.9s using 4 processes
in serial it takes 2.2s

因此,您似乎需要研究一种更有效的方法来将阵列分成多个部分。

我认为有两个主要问题。

  1. 多个进程及其之间进行通信的开销

    生成几个Python解释器会产生一些开销,但是主要是在“工作者”进程之间来回传递数据会降低性能。 通过“ Queue ”的数据需要“腌制”和“未腌制”,这对于较大的数据来说有点慢(您需要这样做两次)。

    如果要使用线程而不是进程,则不需要使用Queue 在CPython中将线程用于CPU繁重的任务通常被认为效率低下,因为通常您会遇到Global Interpreter Lock ,但并非总是如此! 幸运的是,Numpy的排序功能似乎正在释放GIL,因此使用线程是一个可行的选择!

  2. 数据集的分区和联接

    分区和联接数据是这种“桶排序方法”的必然成本,但是可以通过更有效地执行操作来减轻一些负担。 特别是这两行代码

     b = array([bmin[i] <= X, X < bmax[i+1]]).all(0) Y[arange(Bsz[i],Bsz[i+1])] = ... 

    可以重写为

     b = (bmin[i] <= X) & (X < bmax[i+1]) Y[Bsz[i] : Bsz[i+1]] = ... 

    我做了一些改进,我发现np.take比“ fancy indexing”要快, np.partition也很有用。

总而言之,以下是我能做到的最快的速度(但是它仍然不能像您想要的那样随内核数线性增长。):

from threading import Thread

def par_argsort(X, nproc):
    N = len(X)
    k = range(0, N+1, N//nproc)
    I = X.argpartition(k[1:-1])
    P = X.take(I)

    def worker(i):
        s = slice(k[i], k[i+1])
        I[s].take(P[s].argsort(), out=I[s])

    t_list = []
    for i in range(nproc):
        t = Thread(target=worker, args=(i,))
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    return I

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM