Python线程意外地变慢了

时间:2010-06-25 20:07:15

标签: python multithreading parallel-processing

我决定学习如何在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()之类的内容?

3 个答案:

答案 0 :(得分:9)

  1. Python有GIL。 Python字节码一次只能由一个处理器执行。只有某些C模块(不管理Python状态)才能同时运行。
  2. Python GIL在锁定线程之间的状态方面有巨大的开销。在较新的版本或开发分支中有一些修复 - 至少应该使单线程代码的多线程CPU绑定代码快。
  3. 您需要使用多进程框架与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.htmlhttps://ray.readthedocs.io/en/latest/autoscaling.html)。

免责声明:我是Ray开发人员之一。