Python中基于对象流(即仅包含一项的流)的多线程处理

时间:2019-04-10 06:01:15

标签: python multithreading numpy parallel-processing

我想知道如何自动并行化基于对象流的Python程序(给定流定义,无需解析)?通过对象流,我的意思是像流,但它不是连续流,而是输入一个大对象并输出一个大对象(例如numpy.ndarray)。

这在numpy中特别有用,因为numpy进行大向量化操作而不是循环融合。一个计算可能会从多个先前计算中获取输入,而其输出可能会被多个后续计算使用。这些前辈和后继者可以同时(分别)计算。

一个例子是:

a  = np.random.rand(1024)
b1 = a.mean()
b2 = c.std()
c  = (a - b1) / b2

此处b1(平均值)和b2 std可同时计算。对于较大的计算,这非常有用,因为并非numpy中的所有操作都是内部多线程的。

如果程序包可以自动确定计算顺序,将计算分配给内核,并可能避免使用过多的内存导致MemoryError或OOM杀手er,那将很有帮助。使用numpy将绕过Python中的GIL限制,因此多线程是一个不错的选择。

一方面,我正在寻找一些开发的软件包或解决方案来做到这一点。另一方面,我自己编写了一个程序包来尝试解决此问题。代码很简单:

from threading import Thread, Event
class Require:
    def __init__(s, f_worker_map):
        s.d = dict(f_worker_map)
    def get(s, f, arg):
        s.cache = {(f, arg): [Event(), None, 0]}
        s._get(f, arg).join()
        return s.cache[f, arg][1]
    def _get(s, f, arg):
        require = f(*arg)
        for i in require:
            t = s.cache.get(i, None)
            if t:
                t[2] += 1
            else:
                t = [Event(), None, 0]
                s.cache[i] = t
                s._get(*i)
        t = Thread(target=s._thread, args=(s.d[f], arg, require, (f, arg)))
        t.start()
        return t
    def _thread(s, f, arg, require, id):
        for i,v in enumerate(require):
            s.cache[v][0].wait()
            require[i] = s.cache[v][1]
            if s.cache[v][2] < 1:
                del s.cache[v]
            else:
                s.cache[v][2] -= 1
        s.cache[id][1] = f(*arg, *require)
        s.cache[id][0].set()

这里f是一个函数,它以list f-arg的{​​{1}}返回“依赖关系”,而tuple是处理依赖关系的实际计算函数,输出结果。

此代码有效,但不执行内存管理,并且它具有一些有关缓存引用计数器worker的竞争条件问题。竞争条件不是一个严重的问题,但是内存管理是。事实证明这是一个非常复杂的问题,因为它应该通过确定计算顺序来最大程度地减少峰值内存使用量。我想知道是否对此有任何理论或实践?

谢谢!

0 个答案:

没有答案