我想知道如何自动并行化基于对象流的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
的竞争条件问题。竞争条件不是一个严重的问题,但是内存管理是。事实证明这是一个非常复杂的问题,因为它应该通过确定计算顺序来最大程度地减少峰值内存使用量。我想知道是否对此有任何理论或实践?
谢谢!