Using threading to slice an array into chunks and perform calculation on each chunk and reassemble the returned arrays into one array
我有一个大的python数组,我想分成多个块,然后对这些块执行计算,然后"重新组装"为一个数组。 到目前为止,这里是我到目前为止刚开始学习线程和Python线程的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def performCalc(binaryArray): # perform some operation rArray = blah * binaryArray return rArray def main(argv): numberOfThreads = 5 str(len(grey_arr) # 100,000 elements greyScaleChunks = np.array_split(grey_arr, numberOfThreads) for i in range(numberOfThreads): t = Thread(target=performCalc, args=(greyScaleChunks[i],)) t.start() # take all the return values and later create one big array to be resized into matrix. |
块的顺序很重要,我必须保持这一点。
如果要使用显式的
1 2 3 4 5 6 7 8 | ts = [] for i in range(numberOfThreads): t = Thread(target=performCalc, args=(greyScaleChunks[i],)) ts.append(t) t.start() for t in ts: t.join() # When you get here, all threads have finished |
同样,
1 2 3 4 5 6 7 8 9 10 11 | class ReturningThread(threading.Thread): def run(self): try: if self._target: self._result = self._target(*self._args, **self._kwargs) finally: del self._target, self._args, self._kwargs def join(self): super().join() return self._result |
这是未经测试的代码,但是应该可以工作。 (我已经在真实代码中做过类似的事情,但是更加复杂,以允许
所以:
1 2 3 4 5 6 7 8 | ts = [] for i in range(numberOfThreads): t = ReturningThread(target=performCalc, args=(greyScaleChunks[i],)) ts.append(t) t.start() results = [] for t in ts: results.append(t.join()) |
现在,您有了可以堆叠在一起的阵列列表。
但是,我在上面所做的基本上是将每个线程变成半途而废的未来。仅使用实际的期货在概念上可能会更简单。这确实意味着我们现在正在使用一个我们实际上并不需要的线程池,每个线程只有一个任务。性能成本可能可以忽略不计(您在实际工作上花费的时间比在队列上花费的时间多,或者您不想首先以这种方式进行线程化),但是,更重要的是,我们要增加隐藏在引擎盖下(在经过良好测试的stdlib模块中)的额外复杂性,以减少我们代码的复杂性;是否值得,取决于您。无论如何:
1 2 | with concurrent.futures.ThreadPoolExecutor(max_workers=numberOfThreads) as x: results = x.map(performCalc, greyScaleChunks) |
这将处理创建5个线程,为每个
使用执行程序的另一个好处是,如果事实证明您的代码由于GIL而没有从线程并行中受益(在您的情况下不太可能是一个问题-您应该将大部分时间花在20000以上的numpy操作上)行,这些行将在发布GIL时运行-但显然您必须进行测试以验证这是正确的),您可以非常轻松地切换到进程:只需将
您的args和return可能无法以默认方式在进程之间复制或共享,或者这样做的代价太高了,以至于丧失了并行性的所有好处-但事实是您可以用一个单词来测试进行更改,然后仅在有问题的情况下进行处理,这仍然是一个胜利。
您可以使用很大程度上未公开说明的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import numpy as np from pprint import pprint from multiprocessing.pool import ThreadPool import threading blah = 2 def performCalc(binaryArray): # perform some operation rArray = blah * binaryArray return rArray def main(data_array): numberOfThreads = 5 pool = ThreadPool(processes=numberOfThreads) greyScaleChunks = np.array_split(data_array, numberOfThreads) results = pool.map_async(performCalc, greyScaleChunks) pool.close() pool.join() # Block until all threads exit. # Final results will be a list of arrays. pprint(results.get()) grey_arr = np.array(range(50)) main(grey_arr) |
打印结果:
1 2 3 4 5 | [array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]), array([20, 22, 24, 26, 28, 30, 32, 34, 36, 38]), array([40, 42, 44, 46, 48, 50, 52, 54, 56, 58]), array([60, 62, 64, 66, 68, 70, 72, 74, 76, 78]), array([80, 82, 84, 86, 88, 90, 92, 94, 96, 98])] |