在这里记录几种测试Python程序性能的方法。
1、集成开发工具IDE,例:PyCharm。
现在的IDE基本上能满足开发人员的各种基本需求,当然了,性能测试也不例外,PyCharm作为Pythoner的主流IDE,这样的需求也是不在话下,编写好你的程序之后,至于要右击你的代码页面,找到 profile xxx
选项,其中 xxx
为你的代码文件名,点击后,它会弹出一个性能测试页面,里面包含了你代码中各个耗时操作的性能分析,包括一些堆栈调用。
2、使用Unix 系统自带的时间工具time。
同样的,假设现在我们有这样的一个代码文件 code.py
,代码如下:
a = [i * 2 for i in range(10000000)]
我们在Unix系统中使用如下命令行运行上面的代码文件:
time python code.py
运行结果如下:
real 0m0.132s
user 0m0.000s
sys 0m0.031s
这三个结果分别表示:
-
real 记录了程序运行整体的耗时;
-
user 记录了 CPU 花在任务上的时间,但不包括内核函数耗费的时间;
-
sys 记录了内核函数耗费的时间。
3、使用Python内置的模块,如:time
、timeit
。
相对来说 time
模块大家都比较熟悉,其中有几个方法可供大家调用:
-
time.time()
:import time s = time.time() a = [i ** 2 for i in range(1000000)] e = time.time() print(f"s: {s}") print(f"e: {e}") print(f"Time cost {e - s} s.")
结果如下:
s: 1671764979.9903982 e: 1671764980.7124395 Time cost 0.7220413684844971 s.
-
time.perf_counter()
:import time s = time.perf_counter() a = [i ** 2 for i in range(1000000)] e = time.perf_counter() print(f"s: {s}") print(f"e: {e}") print(f"Time cost {e - s} s.")
结果如下:
s: 2.054608198544187e-06 e: 0.7287198065052183 Time cost 0.7287177518970197 s.
-
time.process_time()
:import time s = time.process_time() a = [i ** 2 for i in range(1000000)] e = time.process_time() print(f"s: {s}") print(f"e: {e}") print(f"Time cost {e - s} s.")
结果如下:
s: 0.09360059999999999 e: 0.7332046999999999 Time cost 0.6396040999999999 s.
我们在代码中加上
time.sleep
:import time s = time.process_time() a = [i ** 2 for i in range(1000000)] time.sleep(3) e = time.process_time() print(f"s: {s}") print(f"e: {e}") print(f"Time cost {e - s} s.")
结果如下:
s: 0.0780005 e: 0.7644048999999999 Time cost 0.6864043999999999 s.
好了,接下来就是对比三者之间的区别,通过上面的代码相信大家应该轻松就能应用起来:
time.time()
:返回当前时间的时间戳(1970纪元后经过的浮点秒数),计算程序性能时,包含系统休眠时间。time.perf_counter()
:从当前计算机系统中选取一个时间点,在测试耗时较短的代码性能上,具有最高可用分辨率。计算程序性能时,包含系统休眠时间。只有在连续调用结果之间的差异才有效。time.process_time()
:返回当前进程的系统和用户CPU时间总和的值(以小数秒为单位)。它不包括睡眠期间经过的时间。同样的,只有在连续调用结果之间的差异才有效。
接下来是 timeit
模块:
比较重要的两个方法的源码如下:
def timeit(stmt="pass", setup="pass", timer=default_timer,
number=default_number, globals=None):
"""Convenience function to create Timer object and call timeit method."""
return Timer(stmt, setup, timer, globals).timeit(number)
def repeat(stmt="pass", setup="pass", timer=default_timer,
repeat=default_repeat, number=default_number, globals=None):
"""Convenience function to create Timer object and call repeat method."""
return Timer(stmt, setup, timer, globals).repeat(repeat, number)
参数释义:
stmt
:用于传入要测试时间的代码,可以直接接受字符串的表达式,也可以接受单个变量,也可以接受函数。传入函数时要把函数申明在当前文件中,然后在stmt = func()
执行函数,然后使用setup = from __main__ import func
setup
:传入stmt的运行环境,比如stmt中使用到的参数、变量,要导入的模块等。可以写一行语句,也可以写多行语句,写多行语句时要用分号;隔开语句。number
:要测试的代码的运行次数,默认100000次,对于耗时的代码,运行太多次会比较慢,此时建议自己修改一下运行次数repeat
:指测试要重复几次,每次的结果构成列表返回,默认3次。
- 直接构建测试语句并运行:
import timeit
stat1 = timeit.timeit(stmt="[i ** 2 for i in range(N)]", setup="N = 1000000", number=10)
print(f"Test sentence 1: {stat1}")
stat2 = timeit.repeat(stmt="[i ** 2 for i in range(N)]", setup="N = 1000000", number=10, repeat=2)
print(f"Test sentence 2: {stat2}")
# 复合语句作为代码块
stat3 = timeit.timeit(stmt="[i + bonus for i in range(N)]", setup="N = 1000000;bonus=100", number=10)
print(f"Test sentence 3: {stat3}")
stat4 = timeit.repeat(stmt="[i + bonus for i in range(N)]", setup="N = 1000000;bonus=100", number=10, repeat=2)
print(f"Test sentence 4: {stat4}")
# 以函数调用的形式完成统计
def seeker():
N = 1000000
bonus = 100
return [i + bonus for i in range(N)]
stat5 = timeit.timeit(stmt="seeker()", setup="from __main__ import seeker", number=10)
print(f"Test sentence 5: {stat5}")
stat6 = timeit.repeat(stmt="seeker()", setup="from __main__ import seeker", number=10, repeat=3)
print(f"Test sentence 6: {stat6}")
运行结果如下:
Test sentence 1: 5.334041077370792
Test sentence 2: [5.435822258310274, 5.48210682812052]
Test sentence 3: 1.6497468311777759
Test sentence 4: [1.5984531265794786, 1.5951452073798258]
Test sentence 5: 1.6178880764511483
Test sentence 6: [1.599862998725321, 1.590234282863662, 1.5924134003190389]
- 先构造
pipline
实例,这里有特定的方法Timer
,再传入指定参数并运行:
import timeit
timer1 = timeit.Timer(stmt="[i + bonus for i in range(N)]", setup="N = 1000000;bonus = 100")
stat1 = timer1.timeit(number=10)
print(f"Test sentence 1: {stat1}")
stat2 = timer1.repeat(number=10, repeat=2)
print(f"Test sentence 2: {stat2}")
# 使用函数的方式进行调用
def seeker():
N = 1000000
bonus = 100
return [i + bonus for i in range(N)]
timer2 = timeit.Timer(stmt="seeker()", setup="from __main__ import seeker")
stat3 = timer2.timeit(number=10)
print(f"Test sentence 3: {stat3}")
stat4 = timer2.repeat(number=10, repeat=2)
print(f"Test sentence 4: {stat4}")
运行结果如下:
Test sentence 1: 1.7151524067269517
Test sentence 2: [1.6136428449913172, 1.5879380527409706]
Test sentence 3: 1.59479880043755
Test sentence 4: [1.5951669862267277, 1.5980076875220348]
以上,大家可以根据自己的喜好去选择使用对应的测试方法。