多线程Python脚本耗时比非线程脚本长
免责声明:我对多线程的理解不是很好,所以我可能做错了什么。
我用Python写了一个非常基础的光线追踪器,想找一些方法来加快它的速度。多线程看起来是个不错的选择,于是我决定试试看。然而,原来的脚本处理我的示例场景大约需要85秒,而多线程的版本却花了大约125秒,这让我觉得很奇怪。
这是原始版本的代码(我不打算把绘图逻辑和其他内容复制过来。如果有人觉得需要这些内容来找出问题,我可以再加上):
def getPixelColor(x, y, scene):
<some raytracing code>
def draw(outputFile, scene):
<some file handling code>
for y in range(scene.getHeight()):
for x in range(scene.getWidth()):
pixelColor = getPixelColor(x, y, scene)
<write pixelColor to image file>
if __name__ == "__main__":
scene = readScene()
draw(scene)
这是多线程版本的代码:
import threading
import Queue
q = Queue.Queue()
pixelDict = dict()
class DrawThread(threading.Thread):
def __init__(self, scene):
self.scene = scene
threading.Thread.__init__(self)
def run(self):
while True:
try:
n, x, y = q.get_nowait()
except Queue.Empty:
break
pixelDict[n] = getPixelColor(x, y, self.scene)
q.task_done()
def getPixelColor(x, y, scene):
<some raytracing code>
def draw(outputFile, scene):
<some file handling code>
n = 0
work_threads = 4
for y in range(scene.getHeight()):
for x in range(scene.getWidth()):
q.put_nowait((n, x, y))
n += 1
for i in range(work_threads):
t = DrawThread(scene)
t.start()
q.join()
for i in range(n)
pixelColor = pixelDict[i]
<write pixelColor to image file>
if __name__ == "__main__":
scene = readScene()
draw(scene)
我是不是做错了什么明显的事情?或者我是不是错误地认为多线程会让这个过程更快?
3 个回答
我觉得你可能遇到了两个问题(或者其实是两个都有)。首先,我同意Joe的看法,全局解释器锁可能是导致问题的原因。
其次,看起来在这个过程中你频繁地写文件(特别是在非线程版本中,每次内循环都在写)。你有没有可能是磁盘速度慢,而不是CPU慢?如果是这样的话,当你加入线程后,实际上是增加了管理线程的开销,但并没有解决真正的瓶颈。在优化的时候,确保你先找出瓶颈,这样你才能大致猜测哪些问题解决起来能带来最大的效果。
直接回答你的问题,Python 的多线程并不会提高性能,反而可能因为全局解释器锁(GIL)的存在让情况变得更糟。
从更大的角度来看,我很喜欢 Python 和光线追踪,但你绝对不应该把它们结合在一起。用 Python 写的光线追踪程序,速度至少会比用 C 或 C++ 写的慢两个数量级。
所以虽然你的问题从 Python 程序员的角度来看很有趣,但从光线追踪的角度来看就显得有点搞笑了。
我怀疑Python的全局解释器锁(GIL)让你的代码不能同时在两个线程中运行。
显然,你想利用多个CPU的优势。你能不能把光线追踪的工作分散到不同的进程中,而不是线程里?
多线程版本显然做了更多的“工作”,所以我觉得在单个CPU上它会更慢。
我也不喜欢继承Thread
类,而是直接用t = Thread(target=myfunc); t.run()
来创建一个新线程。