eventlet引发的学习:python:单线程、多线程、多进程在计算方面的性能对比

时间:2022-03-30 16:39:45

参考:

Python GIL 系列之通过实例认识Python的GIL
Python GIL 系列之再谈Python的GIL

概述

本文通过对比测试来证明下单线程、多线程、多进程的‘并发‘计算能力

测试

测试程序

from threading import Thread
from timeit import Timer
from multiprocessing import Process

G_COUNT = 100000000
G_THREAD_COUNT = 2
G_PROCESS_COUNT = 2

def countdown(n):
    while n > 0:
        n -= 1

def thread_start(thread_pool):
    for thread in thread_pool:
        thread.start()

def thread_join(thread_pool):
    for thread in thread_pool:
        thread.join()

def process_start(process_pool):
    thread_start(process_pool)

def process_join(process_pool):
    thread_join(process_pool)


def single_thread():
    count = G_COUNT
    thread_pool = []
    thread = Thread(target=countdown, args=(count,))
    thread_pool.append(thread)

    thread_start(thread_pool)
    thread_join(thread_pool)

def multi_thread():
    count = G_COUNT
    thread_pool = []
    for i in range(G_THREAD_COUNT):
        thread = Thread(target=countdown, args=(count//G_THREAD_COUNT,))
        thread_pool.append(thread)
    thread_start(thread_pool)
    thread_join(thread_pool)

def multi_process():
    count = G_COUNT
    process_pool = []
    for i in range(G_PROCESS_COUNT):
        process = Process(target=countdown, args=(count//G_PROCESS_COUNT,))
        process_pool.append(process)

    process_start(process_pool)
    process_join(process_pool)


def main():
    t = Timer(single_thread)
    print("Single thread:%f" % (t.timeit(1),))

    t = Timer(multi_thread)
    print("Multi thread:%f" % (t.timeit(1),))

    t = Timer(multi_process)
    print("Multi process:%f" % (t.timeit(1),))

if __name__ == "__main__":
    main()

说明

  • 上述程序通过单线程多线程多进程三种方式的并发来测试耗时(CPU密集型操作,非IO型)
  • 上述程序是计算型的测试(通过计算100000000逐一递减),即CPU密集型,而非IO密集型。
  • 多进程

运行结果

上述程序在多核的mac pro上运行结果如下

Single thread:4.297631
Multi thread:5.357289
Multi process:2.101515

说明:上述程序运行多次,测试得到的结果不完全一样,但是基本趋势以及多少对比差不多。

分析

  • 在多核CPU上(注意前提是多核CPU),多线程的耗时比单线程还要长。
  • 多进程比单线程、多线程的计算耗时都要低。

为什么会这样呢?与我们所想象的不一样:多线程应该是并发的,应该耗时比较低才对。

原因

在CPython解释器中有把锁:GIL。因为有所,所以在线程并不能真正的并发,而是串行的,而且在线程切换时时需要费时,所以才会出现这样的结果。

关于GIL的相关内容,请参考这个系列的另外一篇文章eventlet引发的学习:python GIL