timeit:计时小段代码的执行时间
timeit 是 Python 标准库中的一个模块,用于测量小段代码的执行时间。它提供了一种精确、可靠的方式来对代码的性能进行基准测试,避免了诸如系统时间变化和其他外部因素的影响。timeit 可以用于比较不同实现方式的性能,以确定哪种方式更高效。
主要功能
- 精确测量代码执行时间:timeit 使用高精度的计时器来测量代码的执行时间,默认情况下,它会多次执行代码并返回平均时间,以获得更准确的结果。
- 简易使用:该模块提供了简单的接口,可以直接在命令行或者通过 Python 脚本调用,非常方便。
- 避免常见误差:timeit 避免了使用 time 模块时可能遇到的许多误差源,例如时间切片和 CPU 负载变化。
特性与优势
- 高精度:timeit 使用高精度的时钟(在 Windows 上使用perf_counter(),在 Unix 上使用 time.time()),可以精确到微秒级别。
- 多次执行,减少偶然误差:timeit 默认多次执行测试代码,并计算平均时间,以减少由于偶然因素导致的误差。
- 可定制:你可以控制代码执行的次数(number 参数)以及使用的全局和局部命名空间。
- 内置与跨平台支持:timeit 是 Python 标准库的一部分,无需额外安装,并且在所有支持 Python 的平台上都可以使用。
基本用法
timeit 可以直接从命令行使用,也可以在脚本中调用。
命令行用法:
你可以直接在命令行中使用 timeit 模块。例如,要测量 for 循环的性能,可以运行以下命令:
python -m timeit "for i in range(100): pass"
这将会输出代码执行的平均时间。
脚本中使用
你也可以在 Python 脚本中使用 timeit 模块来测量代码块的执行时间:
import timeit # 要测量的代码片段 code = """ result = [] for i in range(100): result.append(i) """ # 执行时间测量 execution_time = timeit.timeit(code, number=1000) print(f"Execution time: {execution_time} seconds")
在这个示例中,timeit.timeit 会多次执行代码(number=1000 表示执行 1000 次),并返回总的执行时间。
使用 Timer 类
timeit 模块中的 Timer 类提供了更灵活的接口,允许你更精确地控制计时的过程:
import timeit def test(): result = [] for i in range(100): result.append(i) t = timeit.Timer("test()", "from __main__ import test") print(t.timeit(number=1000))
在这个示例中,Timer 类的实例被用来测量 test 函数的执行时间。
测量单个语句
你还可以测量单个语句的执行时间:
import timeit execution_time = timeit.timeit('"-".join(str(n) for n in range(100))', number=10000) print(f"Execution time: {execution_time} seconds")
这里测量了将一系列数字转换为字符串并用连字符 – 连接的代码片段的执行时间。
比较不同实现方式
timeit 还可以用于比较不同实现方式的性能。例如,比较使用 for 循环和使用列表推导式生成列表的性能:
import timeit # 使用 for 循环 for_loop_time = timeit.timeit(""" result = [] for i in range(100): result.append(i) """, number=10000) # 使用列表推导式 list_comp_time = timeit.timeit("result = [i for i in range(100)]", number=10000) print(f"For loop time: {for_loop_time} seconds") print(f"List comprehension time: {list_comp_time} seconds")
通过这个对比,你可以看到哪种方式执行更快。
Profile与cProfile:性能分析器
profile和cProfile是Python中用于性能分析(profiling)的模块,它们的主要区别在于实现方式和性能:
- profile: 这个模块是用纯Python实现的。因此,它的代码比较容易阅读和理解,可以作为学习性能分析器实现原理的一个例子。
- cProfile: 这是一个用C语言实现的模块,属于Python标准库的一部分。因为是用C实现的,所以性能要比profile好得多。
在使用上,两者的接口和功能是相似的,通常你可以用类似的方式来使用它们。为了最佳性能分析效果,通常建议使用cProfile。
主要功能
- 函数级别的性能分析:profile 模块能够记录程序中各个函数的调用次数、调用时间以及在每次调用中花费的时间。
- 可视化与分析:分析结果可以保存到文件中,之后可以使用其他工具(如 pstats 模块)进行更详细的分析和可视化。
- 全面的性能测量:profile 模块适合对整个程序的执行进行全面的性能分析,特别是在较大的应用程序中,用于查找最消耗时间的函数。
基本用法
命令行用法
你可以通过命令行直接使用 cProfile 对 Python 脚本进行性能分析。例如,对名为 script.py 的脚本进行分析:
python -m cProfile script.py
这将输出每个函数的调用次数、总时间、每次调用的平均时间等信息。
在代码中使用
你可以在 Python 代码中使用 cProfile 进行性能分析。例如:
import cProfile def my_function(): total = 0 for i in range(10000): total += i return total cProfile.run('my_function()')
这段代码会分析 my_function 的执行时间,并打印性能分析结果。
生成并保存分析报告
你可以将分析结果保存到文件中,之后使用 pstats 模块进行进一步的分析:
import cProfile def my_function(): total = 0 for i in range(10000): total += i return total cProfile.run('my_function()', 'output.prof')
这会将性能分析结果保存到 output.prof 文件中。
使用 pstats 进行详细分析
cProfile 生成的 .prof 文件可以使用 pstats 模块进行更详细的分析。例如:
import pstats p = pstats.Stats('output.prof') p.sort_stats('cumulative').print_stats(10)
使用 Profile 类
cProfile 模块提供了 Profile 类,用于创建性能分析器对象,从而更灵活地控制性能分析过程:
import cProfile def my_function(): total = 0 for i in range(10000): total += i return total profiler = cProfile.Profile() profiler.enable() my_function() profiler.disable() profiler.print_stats()
通过这种方式,你可以手动启动和停止性能分析,并在结束时打印分析结果。
主要功能和方法
- run(statement, filename=None, sort=-1):
- 运行给定的语句并进行性能分析。结果可以保存到指定的文件中。
- statement:要分析的 Python 语句。
- filename:保存结果的文件名。如果为 None,则输出到控制台。
- sort:结果排序方式,可以是 time、cumulative 等。
- Profile():
- 创建 Profile 对象,用于控制性能分析过程。
- enable():
- 启动性能分析。
- disable():
- 停止性能分析。
- print_stats(sort=-1):
- 打印性能分析结果。可以根据 sort 参数进行排序,常见的排序方式有 time、cumulative 等。
pstats:分析器输出格式化
pstats 是 Python 标准库中的一个模块,用于分析和格式化由 cProfile 和 profile 模块生成的性能分析报告。pstats 提供了一些方法,可以对性能数据进行排序、过滤和输出,使得开发者能够更加方便地理解和分析性能瓶颈。
主要功能
- 加载和操作性能数据:通过 pstats,你可以加载之前由 cProfile 或 profile 生成的性能数据文件,并对这些数据进行操作和分析。
- 结果排序和过滤:提供多种排序和过滤选项,帮助开发者查看最重要的性能数据。
- 格式化输出:支持多种格式化输出方式,可以打印出详细的性能统计信息。
特性与优势
- 详细的性能数据分析:pstats 可以详细分析性能数据,包括每个函数的调用次数、总时间、累计时间等。
- 灵活的排序与过滤:提供多种排序和过滤选项,帮助开发者快速找到性能瓶颈。
- 易于使用:pstats 提供了简单的接口来打印和分析性能数据,使得性能分析工作变得更加便捷。
加载性能数据
pstats 可以加载 cProfile 或 profile 生成的 .prof 文件,以进行进一步的分析:
import pstats # 加载性能数据文件 stats = pstats.Stats('output.prof')
排序和过滤
pstats 提供了多种排序选项,可以按不同的标准对性能数据进行排序,以便更好地分析程序的性能瓶颈。常见的排序标准包括:
- ‘time’:按每个函数的总时间排序。
- ‘cumulative’:按每个函数的累计时间排序。
- ‘calls’:按调用次数排序。
例如,要按累计时间排序并打印前 10 个函数的性能数据:
import pstats stats = pstats.Stats('output.prof') stats.sort_stats('cumulative').print_stats(10)
打印性能数据
pstats 提供了多种打印方法,允许开发者以不同的方式查看性能数据:
- print_stats([*args]):打印性能统计信息。args 可以是整数(表示打印的函数数量)或字符串(表示过滤条件)。
- print_callees(funcname):打印指定函数调用的所有子函数的性能数据。
- print_callers(funcname):打印指定函数的所有调用者的性能数据。
例如,要打印调用次数最多的 20 个函数的性能数据:
import pstats stats = pstats.Stats('output.prof') stats.sort_stats('calls').print_stats(20)
过滤和分组
可以使用 pstats 对函数调用进行过滤,以便只查看特定模块或函数的信息。例如,查看某个模块中函数的性能数据:
import pstats stats = pstats.Stats('output.prof') stats.sort_stats('time').print_stats('my_module')
主要方法和功能
- Stats(filename):创建一个 Stats 对象,用于加载性能数据文件。
- sort_stats(*args):按照指定的标准对性能数据进行排序。*args 是排序标准,如 ‘time’、’cumulative’ 等。
- print_stats(*args):打印排序后的性能数据。*args 可以是函数的个数或过滤条件。
- print_callees(funcname):打印指定函数的所有子函数的性能数据。
- print_callers(funcname):打印指定函数的所有调用者的性能数据。