如何改善我的多线程使用以减少运行时间,python

时间:2010-12-15 15:35:52

标签: python multithreading optimization

机器信息

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()

问题

  1. 我应该使用multiprocessing模块而不是线程模块吗?
  2. 有没有办法改进下面的解决方案?

2 个答案:

答案 0 :(得分:4)

当线程数接近核心数时,性能非线性增加的原因可能在于:

some_task()#do processor heavy task  

GIL是围绕I / O重型操作发布的;如果some_task()是CPU绑定的,那么你当时只占用GIL一个线程,从而牺牲了线程的好处(并且可能在太多的上下文切换中浪费时间)。

来自http://docs.python.org/c-api/init.html

  

Python解释器不是完全线程安全的。为了支持多线程Python程序,有一个全局锁,称为全局解释器锁或GIL,它必须由当前线程保存才能安全地访问Python对象。如果没有锁定,即使是最简单的操作也可能导致多线程程序出现问题:例如,当两个线程同时增加同一对象的引用计数时,引用计数最终可能只增加一次而不是两次。

     

因此,规则存在只有获取全局解释器锁的线程可以对Python对象进行操作或调用Python / C API函数。为了支持多线程Python程序,解释器定期释放并重新获取锁 - 默认情况下,每100个字节码指令(可以使用sys.setcheckinterval()更改)。锁也被释放并重新获取可能阻塞的I / O操作,例如读取或写入文件,以便在请求I / O的线程等待I / O操作完成时,其他线程可以运行。

我可能错了,但我的猜测是线程共享相同的GIL,但进程没有。 请尝试使用多处理模块。

答案 1 :(得分:2)

如果你正在做一些CPU密集型任务,那么只有在python中加速才能使用多个进程。

其他替代方案

  • 使用python的不同实现,例如IronPythonJython
  • 将CPU密集型代码写为C模块