一、Python性能检测工具简介
Python 是一种高级动态编程语言,速度比起传统的编译语言稍慢,但是 Python 和众多的优秀标准库、框架,被广泛地用于开发各式各样的应用。
Python 应用广泛,某些应用甚至会需要大量数据的处理,此时需要注意 Python 的运行效率,以免造成内存泄漏(Memory Leak)或超时(Time Out)等问题。因此,需要 Python 中的性能测试来进行 CPU 和内存占用情况的调试和分析。
Python 中有许多测试工具可以提供性能测试和内存检测,下面通过介绍一些实用的测试工具来快速找到 Python 应用程序的瓶颈,及时发现和解决问题。
二、性能检测工具函数运行时间
在 Python 开发中,经常需要计算函数的运行时间,下面给出一个手写计时器的示例代码:
import datetime
# 定义计时器装饰器函数
def func_timer(func):
def wrapper(*args, **kwargs):
print("Running function: {0} ...".format(func.__name__))
# 记录开始时间
start_time = datetime.datetime.now()
# 执行函数
result = func(*args, **kwargs)
# 记录结束时间
end_time = datetime.datetime.now()
# 打印运行时间
print("Function {0} runs for: {1} seconds.".format(func.__name__, (end_time-start_time).total_seconds()))
# 返回函数执行结果
return result
return wrapper
# 定义需计算运行时间的函数
@func_timer
def get_sum(a, b):
return a + b
# 调用示例
print(get_sum(111, 222)) # => Running function: get_sum ... \nFunction get_sum runs for: 0.000019 seconds.\n333
上述代码中,func_timer
函数是一个装饰器,可以给函数添加计时器的功能。其中,wrapper
函数是计时器的核心部分,可以在计时前记录当前时间 start_time
,在函数执行后记录当前时间 end_time
,而两者的时间差即可求得函数的运行时间。只需要将需要计算时间的函数用 @func_timer
装饰即可。
三、性能检测工具函数运行内存
除了计算运行时间外,还需要检测函数的内存占用情况。Python 的内存管理机制是自动化的,但是这并不意味着开发者可以完全不考虑内存占用的问题。下面给出一个示例代码,可以检测函数运行时的内存情况:
import os
import psutil
# 定义查看内存占用的函数
def get_process_memory():
process = psutil.Process(os.getpid())
memory_info = process.memory_info()
return memory_info.rss
# 定义装饰器函数
def func_memory(func):
def wrapper(*args, **kwargs):
# 记录开始时刻内存
before = get_process_memory()
# 执行函数
result = func(*args, **kwargs)
# 记录结束时刻内存
after = get_process_memory()
print("Function {0} uses memory: {1:.2f} KB".format(func.__name__, (after-before)/1024))
return result
return wrapper
# 计算阶乘
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
# 调用检测内存使用的函数
factorial = func_memory(factorial)
# 调用示例
print(factorial(10)) # => Function factorial uses memory: 0.00 KB \n >>> 3628800
上述代码中,get_process_memory
函数用于获取当前进程的内存占用情况,func_memory
则是用于计算内存使用的装饰器函数,它先记录函数执行前的内存 before
,在函数执行之后再次获取当前时刻的内存 after
,并将两者之差作为内存使用量输出到控制台。
将需要检测内存的函数用 func_memory
装饰即可。
四、示例说明
下面介绍两个示例:
- 计算斐波那契数列
斐波那契数列可以递归算出,也可以使用循环算出。下面给出相应的代码示例:
# 递归方式计算斐波那契数列
def fibonacci_recursive(n):
if n<=2:
return 1
else:
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)
print(fibonacci_recursive(20)) # => 6765
使用递归算法计算斐波那契数列,时间复杂度为$O(2^{n-1})$,在 n=20
时即耗时较长(约需3秒),且内存占用较高。通过运行时间检测工具进行性能测试,可以使用如下代码:
# 使用时间检测工具
fibonacci_recursive = func_timer(fibonacci_recursive)
print(fibonacci_recursive(20)) # => Running function: fibonacci_recursive ... \nFunction fibonacci_recursive runs for: 2.935805 seconds.\n >>> 6765
将 fibonacci_recursive
函数用 func_timer
装饰,即可得出函数的运行时间。从结果可以看出,递归方式计算斐波那契数列所需时间较长。
接下来使用循环的方式计算斐波那契数列,时间复杂度为$O(n)$,代码示例如下:
# 循环方式计算斐波那契数列
def fibonacci_loop(n):
if n<=2:
return 1
else:
a, b = 1, 1
for i in range(3, n+1):
a, b = b, a+b
return b
print(fibonacci_loop(20)) # => 6765
使用循环方式计算斐波那契数列,则计算过程明显快了很多,而内存占用也显著减少。此时,可以使用内存检测工具进行性能测试,代码示例如下:
# 使用内存检测工具
fibonacci_loop = func_memory(fibonacci_loop)
print(fibonacci_loop(20)) # => Function fibonacci_loop uses memory: 0.00 KB \n >>> 6765
将 fibonacci_loop
函数用 func_memory
装饰,即可得出函数的内存使用情况。从结果可以看出,循环方式计算斐波那契数列所需内存较小。
- 实现数据类型转换
在 Python 开发中,经常需要进行数据类型的转换。下面给出两种方式进行字符串和列表的转换:
# 字符串和列表的类型转换
s = "hello world"
l = s.split() # 字符串转列表
print(l) # => ['hello', 'world']
s_new = " ".join(l) # 列表转字符串
print(s_new) # => hello world
上述代码中,将字符串 s
转换为列表 l
,并将列表 l
再转换回字符串 s_new
。可以使用时间检测工具和内存检测工具进行性能测试,代码示例如下:
# 使用时间检测工具
s_split = func_timer(s.split)
print(s_split()) # => Running function: split ... \nFunction split runs for: 0.000008 seconds.\n >>> ['hello', 'world']
s_join = func_timer(" ".join)
print(s_join(l)) # => Running function: join ... \nFunction join runs for: 0.000003 seconds.\nhello world
# 使用内存检测工具
s_split = func_memory(s.split)
print(s_split()) # => Function split uses memory: 1.06 KB \n >>> ['hello', 'world']
s_join = func_memory(" ".join)
print(s_join(l)) # => Function join uses memory: 0.91 KB \n >>> hello world
将函数用 func_timer
装饰,即可得出函数的运行时间;将函数用 func_memory
装饰,即可得出函数的内存使用情况。从结果可以看出,字符串和列表的类型转换时间极短,内存占用也较小。
本文链接:https://my.lmcjl.com/post/20858.html
4 评论