高性能环境中分叉工作进程之间的共享状态



这是我上一个问题的后续内容。正如Tim Peters所建议的,使用Manager可能不一定是最好的方法。不幸的是,我有太多的脚手架代码来发布SSCCE。相反,我将尝试对我的问题进行详细解释。请随意浏览Github上的整个代码库,但现在有点乱。

背景

我正在研究自然语言处理,我想为文档分类做一些基于字典的平滑处理。训练分类器将单词和短语与正确答案联系起来的想法。例如,包含单词socialist的文档可能是关于政治的,而包含短语lava temperature的文档则可能是关于地质学的。通过查看少量预先标记的示例来训练系统。由于语言的多样性,分类器永远不会"知道"它在生产中可能遇到的所有可能的短语。

这就是词典的用武之地。假设我们有一种既便宜又简单的方法来获取几乎所有短语的同义词(我会引用我自己的话,因为这很低级)。当一个糟糕的分类器遇到一个它不知道的短语时,我们可以在字典中查找它,并告诉分类器"看,你不知道communism,但它有点像socialist,你知道!"。如果字典是合理的,分类器通常会表现得更好。

伪代码

data = Load training and testing documents (300MB on disk)
dictionary = Load dictionary (200MB - 2GB on disk) and place into a `dict` for fast look-ups
Repeat 25 times:
do_work(data, dictionary)
def do_work(data, dictionary)
X = Select a random sample of data
Train a classifier on X
Y = Select a random sample of data
Using dictionary, classify all documents in Y
Write results to disk

问题

上面的循环是并行化的完美候选者。我一直在使用Python2.7multiprocessing.Pool(通过joblib.Parallel,因为它很简单,并且在出现问题时提供了非常有用的回溯)。所有工作进程都需要对字典和文档集合进行只读访问。工人们不需要相互交流或与父进程交流——他们所做的只是产卵、变魔术、写文件然后死亡。

词典需要支持快速随机访问。我不知道示例Y将包含哪些文档,所以我无法轻松地修剪字典并将其中需要的部分传递给每个工作人员。字典会经常被查询——每次运行的典型命中数是数百万。目前,我的代码是内存绑定的,因为(我相信)正在为每个工作进程制作文档集合和字典的副本。当解析CCD_ 9和CCD_。我曾尝试使用multiprocessing.managers.BaseManager来避免复制大型对象,但这会减慢工作人员的速度。

问题

还有什么其他选择可以加快速度?我考虑过的事情包括:

  • MongoDB/CouchDB/memcached应该能很好地处理并发访问,但我担心吞吐量。zeromq在对我之前的问题的评论中也被建议,我还没有机会深入研究它
  • 内存中的sqlite数据库和数据库连接不能在进程之间共享,因此每个工作进程都需要自己与磁盘上数据库的连接。这意味着一开始需要大量I/O,随着每个工作进程的缓存增长,内存使用率也会很高
  • 内存映射
  • 使用线程而不是进程

这个SO问题还表明,许多看起来需要对dict进行只读访问的现实世界问题可能会触发fork()的写时复制,因此可能无法完全避免对大型对象进行复制。

在您描述的场景中,使用多线程时,由于GIL,您可能会遇到较大的性能问题。可能是为了避免您选择使用多重处理。另一方面,它使用流程,因此可能会为每个子流程复制数据结构。

我不想这么说,但使用非Python解决方案(例如C++)可能会加快速度,因为那里没有GIL问题。然后你可以使用多线程,不必复制东西等。从多个线程中读取大型字典并不是一个真正的问题,所以你不必同步任何东西(GIL总是在没有真正需要的情况下为你做的事情)。

最新更新