如何解决在函数内调用多处理池非常慢
我正在尝试使用 pathos 在函数内触发多处理。然而,我注意到一个奇怪的行为,不知道为什么:
import spacy
from pathos.multiprocessing import Processpool as Pool
nlp = spacy.load("es_core_news_sm")
def preworker(text,nlp):
return [w.lemma_ for w in nlp(text)]
worker = lambda text: preworker(text,nlp)
texts = ["Este es un texto muy interesante en español"] * 10
# Run this in jupyter:
%%time
pool = Pool(3)
r = pool.map(worker,texts)
输出是
cpu times: user 6.6 ms,sys: 26.5 ms,total: 33.1 ms
Wall time: 141 ms
到目前为止一切顺利......现在我定义了相同的精确计算,但是来自一个函数:
def out_worker(texts,nlp):
worker = lambda text: preworker(text,nlp)
pool = Pool(3)
return pool.map(worker,texts)
# Run this in jupyter:
%%time
r = out_worker(texts,nlp)
现在的输出是
cpu times: user 10.2 s,sys: 591 ms,total: 10.8 s
Wall time: 13.4 s
为什么会有这么大的差异?我的假设,虽然我不知道为什么,但在第二种情况下,nlp 对象的副本被发送到每个作业。 >
谢谢
编辑:
import spacy
from pathos.multiprocessing import Processpool as Pool
import time
# Install with python -m spacy download es_core_news_sm
nlp = spacy.load("es_core_news_sm")
def preworker(text,nlp)
texts = ["Este es un texto muy interesante en español"] * 10
st = time.time()
pool = Pool(3)
r = pool.map(worker,texts)
print(f"Usual pool took {time.time()-st:.3f} seconds")
def out_worker(texts,texts)
st = time.time()
r = out_worker(texts,nlp)
print(f"Pool within a function took {time.time()-st:.3f} seconds")
def out_worker2(texts,nlp,pool):
worker = lambda text: preworker(text,nlp)
return pool.map(worker,texts)
st = time.time()
pool = Pool(3)
r = out_worker2(texts,pool)
print(f"Pool passed to a function took {time.time()-st:.3f} seconds")
就我而言,输出是这样的:
Usual pool took 0.219 seconds
Pool within a function took 8.164 seconds
Pool passed to a function took 8.265 seconds
spacy nlp 对象非常重(几 MB)。我的 spacy 版本是 3.0.3
解决方法
代替
from pathos.multiprocessing import ProcessPool as Pool
,我用过
from multiprocess import Pool
,本质上是一样的。然后我尝试了一些替代方法。
所以:
from multiprocess import Pool
对于“通常”情况产生 0.1s
,对于其他两种情况产生 12.5s
。
然而:
from multiprocess import Pool
import dill
dill.settings['recurse'] = True
对于所有三种情况都产生 12.5s
。
最后:
from multiprocess.dummy import Pool
对于所有三种情况都产生 0.1s
。
这告诉我,这绝对是一个序列化问题,而 globals
的序列化是速度的关键。
在第一种情况下,默认的 dill
行为是尽可能避免通过 globals
进行递归。它能够以“通常”的方式成功执行此操作,但对于函数内的其他两个调用则不能。
当我第一次导入 dill
并将全局变量的行为切换为 recurse
时(这就是 cloudpickle
的酸洗方式),然后在所有三个尝试中都很慢(“通常”方式包括)。
最后,如果我使用 multiprocess.dummy
,因此使用 ThreadPool
- 它不需要序列化全局变量,并且您可以看到它在所有情况下都很快。
结论:如果可行,请使用 pathos.pools.ThreadPool
或 multiprocess.dummy.Pool
。否则,请确保以不序列化全局变量的方式运行。
dill
中有一个有用的工具,您可以使用它来查看正在序列化的内容。如果包含 dill.detect.trace(True)
,则 dill 会为它正在序列化的对象吐出一堆代码,因为它递归地腌制对象及其依赖项。您必须查看 dill
源代码以匹配键(例如,F1
是一种特定类型的函数对象,而 D1
是一种特定类型的字典)。您可以看到不同的方法如何序列化不同的底层对象。不幸的是,我没有分析器,因此您无法立即看到速度命中的位置,但您可以看到它采取的不同策略。
我只是尽量避免序列化 nlp
对象,或任何导致速度变慢的对象(可能是 nlp
对象)。
例如,您可以这样做,而不是在函数中传递 nlp 对象:
import spacy
from multiprocess import Pool
import time
# Install with python -m spacy download es_core_news_sm
nlp = spacy.load("es_core_news_sm")
def preworker(text,nlp):
return [w.lemma_ for w in nlp(text)]
worker = lambda text: preworker(text,nlp)
texts = ["Este es un texto muy interesante en espanol"] * 10
st = time.time()
pool = Pool(3)
r = pool.map(worker,texts)
pool.close(); pool.join()
print("Usual pool took {0:.3f} seconds".format(time.time()-st))
def out_worker(texts):
worker = lambda text: preworker(text,nlp)
pool = Pool(3)
res = pool.map(worker,texts)
pool.close(); pool.join()
return res
st = time.time()
r = out_worker(texts)
print("Pool within a function took {0:.3f} seconds".format(time.time()-st))
通过引用查找传递 nlp
而不是通过函数参数显式传递,两种情况的速度都是 0.1s
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。