0%

优化Python运行速度

[TOC]

一、一般流程

第一, 寻找慢的问题点 第二, 针对比较慢的算法, 要具体的深入的进行分析它慢的原因. 也可以采用计时的方法剖析, 庖丁解牛. 第三步, 优化算法 第四步, 优化编译器和语言问题. 第五步, 上硬件.

考虑哪个函数耗时比较长:

1
2
3
4
5
6
import time
t1= time.time()
函数1()
t2= time.time()

print("耗时",t2-t1) #多段打印, 多试几次,

进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程。 线程:进程中的一个执行任务,负责当前进程中程序的执行

1、全局解释器锁原因:如果你的程序只有单线程、单进程,代码的速度和性能不会受到全局解释器锁的影响。

单进程中使用多线程实现并发,并且是 IO 密集型(例如网络 IO 或磁盘 IO)的线程,GIL 竞争的效果就很明显了。[1]

  • PyPy 也是一种带有 GIL 的解释器
  • PyPy 也是一种带有 GIL 的解释器

二、For循环多进程&多线程

image-20220812213731617
任务管理器--性能

1、GIL锁

python始于1991年,创立初期对运算的要求不高,为了解决多线程共享内存的数据安全问题,引入了GIL锁,全称为Global Interpreter Lock,也就是全局解释器锁。

GIL规定,在一个进程中每次只能有一个线程在运行。这个GIL锁相当于是线程运行的资格证,某个线程想要运行,首先要获得GIL锁,然后遇到IO或者超时的时候释放GIL锁,给其余的线程去竞争,竞争成功的线程获得GIL锁得到下一次运行的机会。

正是因为有GIL的存在,python的多线程其实是假的,所以才有人说python的多线程非常鸡肋。[2]

2、多进程

来源1

进程的创建和停止都是消耗资源的所以进程绝不是越多越好。因为单个CPU核某时刻只能执行单个进程,所以最好的情况是将进程数量与CPU核数相等,这样可以最大化利用CPU。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 普通情况
import time

def tes(a): # for循环里的东西先写成函数
a = 2 ** a
time.sleep(0.2) # time.sleep()推迟调用线程的运行,可通过参数secs指秒数
print(a)

if __name__ == '__main__':
t1=time.time()
for i in range(20):
tes(i)
t2=time.time()
print("普通耗时:",t2-t1)

# 普通耗时: 4.1959967613220215

进程池

维持一个水位即进程数是恒定的,不够就补,并且保持在一个上限,不许超出上限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import time
import multiprocessing

def tes(a): # for循环里的东西先写成函数
a = 2 ** a
time.sleep(0.2)
print(a)

if __name__ == '__main__':
t3=time.time()
pool = multiprocessing.Pool(2) # 两个进程执行
# pool = multiprocessing.Pool(multiprocessing.cpu_count()) # 全部cpu执行
for i in range(20):
pool.apply_async(func=tes,args=(i,))
pool.close()
pool.join()
t4=time.time()
print("多进程耗时:",t4-t3)

# 多进程耗时: 2.679999589920044 两进程
# 多进程耗时: 1.9049992561340332 全部CPU
1
2
3
4
5
6
7
8
9
10
11
12
13
# 多进程模板(进程池)
import multiprocessing

def tes(): # 把for循环下的内容写成一个函数
代码

if __name__ == '__main__':
pool = multiprocessing.Pool(2) # 两个进程执行
# pool = multiprocessing.Pool(multiprocessing.cpu_count()) # 全部cpu执行
for i in range(20):
pool.apply_async(func=tes,args=(i,)) # 填写定义的函数、参数
pool.close() # close()方法,进程池不再接受新的任务(注意不是进程)
pool.join() # join()方法,阻塞主进程,一定要在close()或者terminate()之后。

3、多线程

来源1来源2

(96条消息) python多进程和多线程看这一篇就够了_T型人小付的博客-CSDN博客_python多进程和多线程

线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED

def tes(a): # for循环里的东西先写成函数
a = 2 ** a
time.sleep(0.2)
print(a)

if __name__ == '__main__':
ti1=time.time()
executor = ThreadPoolExecutor(5)
all_tasks = [executor.submit(tes, i) for i in range(20)]
wait(all_tasks, return_when=ALL_COMPLETED)
ti2=time.time()
print("多线程耗费时间:",ti2-ti1)

多线程耗费时间: 0.8389759063720703

脚注


  1. 1.为什么 Python 这么慢? - 知乎 (zhihu.com) ↩︎
  2. 2.https://blog.csdn.net/Victor2code/article/details/109005171 ↩︎
+