如何提高我的多线程使用以减少运行时间,Python
机器信息
cpu_num 8 CPUs
cpu_speed 2826 MHz
mem_total 8173980 KB
swap_total 16777208 KB
性能测试
当我增加线程数量时,性能提升的情况看起来像这样(这些数字是经过10次运行的平均值)
Number of Threads Time
1 1.322187
2 0.789151
3 0.72232
5 0.613691
10 0.558912
40 0.531966
运行代码时的系统监控快照
top - 01:40:42 up 7 days, 13:24, 9 users, load average: 0.34, 0.22, 0.27
Tasks: 364 total, 2 running, 362 sleeping, 0 stopped, 0 zombie
Cpu(s): 28.2%us, 0.1%sy, 0.0%ni, 71.5%id, 0.0%wa, 0.1%hi, 0.0%si, 0.0%st
Mem: 8173980k total, 7437664k used, 736316k free, 224748k buffers
Swap: 16777208k total, 149244k used, 16627964k free, 6374428k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20365 ben.long 15 0 723m 208m 4224 S 226.2 2.6 0:37.28 python26
19948 ben.long 15 0 10996 1256 764 R 0.7 0.0 0:03.84 top 4420 ben.long 15 0 106m 3776 1360 R 0.0 0.0 0:03.06 sshd
4421 ben.long 15 0 64320 1628 1180 S 0.0 0.0 0:00.07 bash 4423 ben.long 15 0 64320 1596 1180 S 0.0 0.0 0:00.03 bash
19949 ben.long 15 0 64308 1552 1136 S 0.0 0.0 0:00.01 bash
代码
简化后的代码看起来是这样的
from threading import Thread
class testit(Thread):
def __init__ (self,i):
Thread.__init__(self)
def run(self):
some_task()#do processor heavy task
num_threads_to_use = 10
thread_list = []
for i in range (0,num_threads_to_use):
current = testit(i)
thread_list.append(current)
current.start()
for thread in thread_list:
thread.join()
问题
- 我应该使用 multiprocessing 模块,而不是线程模块吗?
- 有没有办法改进下面的解决方案?
2 个回答
2
如果你在做一些需要大量计算的任务,想要在Python中加快速度,唯一的方法就是使用多个进程。
其他的选择有:
- 使用不同版本的Python,比如 IronPython 或者 Jython
- 把需要大量计算的代码写成C语言的模块
4
当线程数量接近核心数量时,性能增长不成比例的原因可能在于以下几点:
some_task()#do processor heavy task
在进行大量输入输出操作时,GIL(全局解释器锁)会被释放;如果某个任务是CPU密集型的,那么每次只有一个线程能占用GIL,这样就牺牲了多线程的好处(而且可能因为频繁切换线程而浪费时间)。
根据http://docs.python.org/c-api/init.html的说明:
Python解释器并不是完全线程安全的。为了支持多线程的Python程序,存在一个全局锁,叫做全局解释器锁(GIL),当前线程必须持有这个锁才能安全地访问Python对象。如果没有这个锁,即使是最简单的操作在多线程程序中也可能出问题:比如,当两个线程同时增加同一个对象的引用计数时,引用计数可能只会增加一次,而不是两次。
因此,有个规则是,只有获得全局解释器锁的线程才能操作Python对象或调用Python/C API函数。为了支持多线程的Python程序,解释器会定期释放和重新获取这个锁——默认情况下,每执行100条字节码指令就会这样做(这个可以通过sys.setcheckinterval()来改变)。在进行可能会阻塞的输入输出操作时,比如读取或写入文件,锁也会被释放和重新获取,这样其他线程就可以在请求输入输出的线程等待操作完成时运行。
我可能错了,但我猜测线程是共享同一个GIL的,而进程则不是。可以尝试使用多进程模块。