关键词

python性能检测工具函数运行内存及运行时间

一、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 装饰即可。

四、示例说明

下面介绍两个示例:

  1. 计算斐波那契数列

斐波那契数列可以递归算出,也可以使用循环算出。下面给出相应的代码示例:

# 递归方式计算斐波那契数列
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 装饰,即可得出函数的内存使用情况。从结果可以看出,循环方式计算斐波那契数列所需内存较小。

  1. 实现数据类型转换

在 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 装饰,即可得出函数的内存使用情况。从结果可以看出,字符串和列表的类型转换时间极短,内存占用也较小。

本文链接:http://task.lmcjl.com/news/5042.html

展开阅读全文