我决定学习如何在Python中完成多线程,我做了一个比较,看看我会在双核CPU上获得什么样的性能提升。我发现我的简单多线程代码实际上比顺序等效运行得慢,我无法弄清楚原因。
我设计的测试是生成一个大的随机数列表,然后打印最大值
from random import random
import threading
def ox():
print max([random() for x in xrange(20000000)])
在我的英特尔酷睿2双核处理器上完成 ox()
需要大约6秒钟,而ox();ox()
大约需要12秒。
然后我尝试从两个线程调用ox()来查看完成的速度。
def go():
r = threading.Thread(target=ox)
r.start()
ox()
go()
大约需要18秒才能完成,两个结果会在1秒内完成打印。为什么这会变慢?
我怀疑ox()
是自动并行化的,因为我查看Windows任务管理器性能选项卡,并在我的python控制台中调用ox()
,两个处理器都会跳到大约75%的利用率,直到它完成。 Python是否会自动并行化max()
之类的内容?
答案 0 :(得分:9)
您需要使用多进程框架与Python并行化。幸运的是,Python附带的multiprocessing模块使得它非常简单。
很少有语言可以自动并行化表达式。如果这是你想要的功能,我建议使用Haskell(Data Parallel Haskell)
答案 1 :(得分:1)
问题出在函数random() 如果从代码中删除随机数。 两个核都试图访问随机函数的共享状态。 核心工作因此而且在缓存同步上花费了大量时间。 这种行为称为虚假共享。 阅读这篇文章False Sharing
答案 2 :(得分:0)
正如Yann正确指出的那样,在此示例中,Python GIL阻止了并行化的发生。您可以使用python多处理模块来解决此问题,或者如果您愿意使用其他开放源代码库,Ray也是解决GIL问题的好选择,并且比起GIL问题更易于使用和具有更多功能Python多处理库。
这是如何使用Ray并行化代码示例:
from random import random
import ray
ray.init()
@ray.remote
def ox():
print(max([random() for x in range(20000000)]))
%time x = ox.remote(); y = ox.remote(); ray.get([x, y])
在我的机器上,您发布的单线程ox()代码花费1.84秒,而使用ray的两次调用花费了1.87s,所以我们在这里得到了几乎完美的并行化。
Ray还使在任务之间共享数据非常有效,在一台计算机上,Ray将在后台使用共享内存,请参见https://ray-project.github.io/2017/10/15/fast-python-serialization-with-ray-and-arrow.html。
您还可以在群集或云上的不同计算机上运行同一程序,而无需修改程序,请参阅文档(https://ray.readthedocs.io/en/latest/using-ray-on-a-cluster.html和https://ray.readthedocs.io/en/latest/autoscaling.html)。
免责声明:我是Ray开发人员之一。