多线程Python脚本耗时比非线程脚本长

3 投票
3 回答
1201 浏览
提问于 2025-04-16 02:39

免责声明:我对多线程的理解不是很好,所以我可能做错了什么。

我用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 个回答

2

我觉得你可能遇到了两个问题(或者其实是两个都有)。首先,我同意Joe的看法,全局解释器锁可能是导致问题的原因。

其次,看起来在这个过程中你频繁地写文件(特别是在非线程版本中,每次内循环都在写)。你有没有可能是磁盘速度慢,而不是CPU慢?如果是这样的话,当你加入线程后,实际上是增加了管理线程的开销,但并没有解决真正的瓶颈。在优化的时候,确保你先找出瓶颈,这样你才能大致猜测哪些问题解决起来能带来最大的效果。

2

直接回答你的问题,Python 的多线程并不会提高性能,反而可能因为全局解释器锁(GIL)的存在让情况变得更糟。

从更大的角度来看,我很喜欢 Python 和光线追踪,但你绝对不应该把它们结合在一起。用 Python 写的光线追踪程序,速度至少会比用 C 或 C++ 写的慢两个数量级。

所以虽然你的问题从 Python 程序员的角度来看很有趣,但从光线追踪的角度来看就显得有点搞笑了。

8

我怀疑Python的全局解释器锁(GIL)让你的代码不能同时在两个线程中运行。

什么是全局解释器锁(GIL)?

显然,你想利用多个CPU的优势。你能不能把光线追踪的工作分散到不同的进程中,而不是线程里?

多线程版本显然做了更多的“工作”,所以我觉得在单个CPU上它会更慢。

我也不喜欢继承Thread类,而是直接用t = Thread(target=myfunc); t.run()来创建一个新线程。

撰写回答