news 2026/3/23 9:59:25

[python] 代码性能分析工具line_profiler使用指北

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[python] 代码性能分析工具line_profiler使用指北

码分析能够评估各部分代码的时间消耗,即进行时间复杂度分析。通过这一过程,我们可以识别影响整体运行效率的关键部分,从而更高效地利用底层计算资源。此外,代码分析也可用于评估内存使用情况,即空间复杂度,以优化内存管理并提升其使用效率。本文主要关注时间复杂度分析的内容。

Python默认提供了两个实用的性能分析库:cProfile和profile。它们能够按函数维度输出性能数据,但无法展示函数内部逐行的执行详情。而Python公开库line_profiler弥补了这一不足,使我们能够清晰查看代码中每一行的执行耗时。

line_profiler是Python中一款强大的逐行性能分析工具,能够帮助开发者精确定位代码中的性能瓶颈。对于初学者而言,该工具在代码优化过程中具有重要价值。line_profiler官方开源仓库见:line_profiler-github,官方文档见:line_profiler-doc。

data

本文将提供一份简单易用的line_profiler教程,重点讲解其在代码中的使用方法;命令行端的使用方式,可参考:Line by Line Profiling of Python Code。line_profiler安装命令如下:

pip install line_profiler

import line_profiler

# 查看line_profiler版本

line_profiler.__version__

'4.1.3'

目录

1 使用入门

1.1 基础使用

1.2 进阶使用

2 参考

1 使用入门

1.1 基础使用

简单调用

以下代码为示例耗时代码,它会经过不同时长的休眠,随后生成大量随机数并计算其平均值。

import time

import math

import random

def power_sum(a, b):

"""计算a到b的平方和"""

return sum(math.pow(x, 2) for x in range(a, b))

def calculate(sleep_time):

time.sleep(sleep_time)

# 随机范围计算平方和的平均值

start = 0

end = 1000000

total = power_sum(start, end)

return total / (end - start)

def test_func():

calculate(sleep_time=1)

calculate(sleep_time=3)

calculate(sleep_time=5)

使用line_profiler分析函数运行时间,首先创建一个LineProfiler对象,再通过调用该实例并传入目标函数,生成一个分析器。执行该分析器即可自动运行目标函数。函数执行完成后,调用分析器实例的print_stats方法即可输出分析结果。注意:直接调用print_stats仅会显示目标函数中顶层代码的运行时间。

from line_profiler import LineProfiler

lprofiler = LineProfiler()

lp_wrapper = lprofiler(test_func)

lp_wrapper()

lprofiler.print_stats()

Timer unit: 1e-09 s

Total time: 9.92798 s

File: /tmp/ipykernel_14853/1826372064.py

Function: test_func at line 17

Line # Hits Time Per Hit % Time Line Contents

==============================================================

17 def test_func():

18 1 1242792669.0 1e+09 12.5 calculate(sleep_time=1)

19 1 3340978413.0 3e+09 33.7 calculate(sleep_time=3)

20 1 5344205767.0 5e+09 53.8 calculate(sleep_time=5)

print_stats输出的各列含义如下:

Timer unit

时间单位(默认自动根据代码执行速度调整),常见的有秒(s)、毫秒(ms,1e-3 秒)、微秒(us,1e-6 秒)、纳秒(ns,1e-9 秒)

Total time

整个函数的总执行时间

Line #

代码行号

Hits

该行代码的执行次数(循环体通常次数较多,函数定义、return等语句通常为1次)

Time

该行代码的累计执行时间(单位同Timer unit),包含该行调用的所有函数耗时

Per Hit

每次执行该行代码的平均时间,计算公式为Time / Hits

% Time

该行耗时占函数总时间的百分比,是识别性能瓶颈的关键指标,需重点关注占比较高的行,循环体通常是性能瓶颈所在

Line Contents

代码内容本身

深层调用

使用line_profiler分析函数运行时间时,如需同时分析其调用的下一层或更深层函数,则需在创建LineProfiler对象时明确指定所有待追踪的目标函数及其子函数。此时,line_profiler会为每个被监控的函数分别生成分析报告,且父函数的耗时统计中将包含所有子函数的执行时间。

from line_profiler import LineProfiler

lprofiler = LineProfiler()

lprofiler.add_function(calculate)

lprofiler.add_function(power_sum)

lp_wrapper = lprofiler(test_func)

lp_wrapper()

# 追踪的函数列表

lprofiler.functions

# output_unit:设置输出单位,可选值,1(秒),1e-3(毫秒),1e-6(微秒)

# details:是否显示详细的行级分析信息,默认是

# summarize:是否对结果进行汇总,默认否

# rich:是否使用富文本格式输出,默认否

lprofiler.print_stats(output_unit=1e-3, details=True, summarize=False, rich=False)

Timer unit: 0.001 s

Total time: 0.756163 s

File: /tmp/ipykernel_14853/1826372064.py

Function: power_sum at line 5

Line # Hits Time Per Hit % Time Line Contents

==============================================================

5 def power_sum(a, b):

6 """计算a到b的平方和"""

7 3 756.2 252.1 100.0 return sum(math.pow(x, 2) for x in range(a, b))

Total time: 9.76247 s

File: /tmp/ipykernel_14853/1826372064.py

Function: calculate at line 9

Line # Hits Time Per Hit % Time Line Contents

==============================================================

9 def calculate(sleep_time):

10 3 9006.3 3002.1 92.3 time.sleep(sleep_time)

11 # 随机范围计算平方和的平均值

12 3 0.0 0.0 0.0 start = 0

13 3 0.0 0.0 0.0 end = 1000000

14 3 756.2 252.1 7.7 total = power_sum(start, end)

15 3 0.0 0.0 0.0 return total / (end - start)

Total time: 9.76259 s

File: /tmp/ipykernel_14853/1826372064.py

Function: test_func at line 17

Line # Hits Time Per Hit % Time Line Contents

==============================================================

17 def test_func():

18 1 1250.6 1250.6 12.8 calculate(sleep_time=1)

19 1 3253.7 3253.7 33.3 calculate(sleep_time=3)

20 1 5258.2 5258.2 53.9 calculate(sleep_time=5)

使用装饰器调用

这种以分析器实例作为装饰器的方式,实质上是一种语法糖。它将被装饰的函数包装为一个代理,使分析器能够监听每次调用,从而精确记录其每一行的执行情况。

from line_profiler import LineProfiler

# 创建性能分析器

lprofiler = LineProfiler()

# 使用装饰器标记要分析的函数

@lprofiler

def fibonacci(n):

if n <= 1:

return n

return fibonacci(n-1) + fibonacci(n-2)

# 没有装饰器标记的函数

def run_analysis():

result = fibonacci(10)

print(f"Fibonacci result: {result}")

# 执行并获取分析结果

run_analysis()

lprofiler.print_stats()

Fibonacci result: 55

Timer unit: 1e-09 s

Total time: 0.000116988 s

File: /tmp/ipykernel_14853/2361721755.py

Function: fibonacci at line 7

Line # Hits Time Per Hit % Time Line Contents

==============================================================

7 @lprofiler

8 def fibonacci(n):

9 177 31407.0 177.4 26.8 if n <= 1:

10 89 10762.0 120.9 9.2 return n

11 88 74819.0 850.2 64.0 return fibonacci(n-1) + fibonacci(n-2)

分析类中的方法

使用line_profiler分析类方法时,需遵循特定流程:首先,必须将目标类的类方法在分析器中显式注册;其次,这些方法必须绑定到具体的类实例上。执行分析时,应通过分析器返回的包装器函数来触发,而非直接调用原始方法,以确保性能数据能被正确捕获。

from line_profiler import LineProfiler

class DataProcessor:

def __init__(self, data):

self.data = data

self.cache = {}

def process_data(self):

"""处理数据的主方法"""

return [self._process_single_item(item) for item in self.data]

def _process_single_item(self, item):

"""处理单个数据项"""

if item not in self.cache:

self.cache[item] = self._complex_calculation(item)

return self.cache[item]

def _complex_calculation(self, x):

"""模拟复杂计算"""

return sum((x + i) **2 for i in range(100))

# 初始化并分析

processor = DataProcessor(list(range(100)))

lprofiler = LineProfiler(

processor.process_data,

processor._process_single_item,

processor._complex_calculation

)

lp_wrapper = lprofiler(processor.process_data)

results = lp_wrapper()

print(f"Processed {len(results)} items")

lprofiler.print_stats()

Processed 100 items

Timer unit: 1e-09 s

Total time: 0.00321407 s

File: /tmp/ipykernel_14853/2028146768.py

Function: process_data at line 8

Line # Hits Time Per Hit % Time Line Contents

==============================================================

8 def process_data(self):

9 """处理数据的主方法"""

10 1 3214073.0 3e+06 100.0 return [self._process_single_item(item) for item in self.data]

Total time: 0.00308825 s

File: /tmp/ipykernel_14853/2028146768.py

Function: _process_single_item at line 12

Line # Hits Time Per Hit % Time Line Contents

==============================================================

12 def _process_single_item(self, item):

13 """处理单个数据项"""

14 100 19064.0 190.6 0.6 if item not in self.cache:

15 100 3049582.0 30495.8 98.7 self.cache[item] = self._complex_calculation(item)

16 100 19599.0 196.0 0.6 return self.cache[item]

Total time: 0.00296471 s

File: /tmp/ipykernel_14853/2028146768.py

Function: _complex_calculation at line 18

Line # Hits Time Per Hit % Time Line Contents

==============================================================

18 def _complex_calculation(self, x):

19 """模拟复杂计算"""

20 100 2964705.0 29647.0 100.0 return sum((x + i) **2 for i in range(100))

使用上下文管理器控制分析范围

from line_profiler import LineProfiler

def expensive_operation(n):

"""昂贵的操作"""

result = 0

for i in range(n):

# 模拟CPU密集型工作

for j in range(1000):

result += (i * j) % (n + 1)

return result

def quick_operation(data):

"""快速操作"""

return [x * 2 for x in data]

lprofiler = LineProfiler(expensive_operation) # 直接在初始化时指定要分析的函数

# 使用with语句启动性能分析上下文,仅对该上下文中的代码进行分析

with lprofiler:

result1 = expensive_operation(1000)

print(f"Expensive operation result: {result1}")

# 不分析的快速操作

print(f"Quick operation result: {quick_operation(range(10))}")

lprofiler.print_stats()

Expensive operation result: 497084589

Quick operation result: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Timer unit: 1e-09 s

Total time: 0.379707 s

File: /tmp/ipykernel_14853/795522087.py

Function: expensive_operation at line 3

Line # Hits Time Per Hit % Time Line Contents

==============================================================

3 def expensive_operation(n):

4 """昂贵的操作"""

5 1 512.0 512.0 0.0 result = 0

6 1001 164615.0 164.5 0.0 for i in range(n):

7 # 模拟CPU密集型工作

8 1001000 145481901.0 145.3 38.3 for j in range(1000):

9 1000000 234060120.0 234.1 61.6 result += (i * j) % (n + 1)

10 1 242.0 242.0 0.0 return result

手动控制调用与结果保存

在手动控制模式下,LineProfiler类的enable_by_count与disable_by_count方法分别用于启动和终止性能计数,显式调用二者可直接对中间代码段进行性能分析;因无需依赖包装函数,能更灵活地控制分析范围。

from line_profiler import LineProfiler

import time

import math

# 创建一个示例模块的函数

def process_data(n):

"""模拟一个需要性能分析的函数"""

result = 0

# 模拟一些计算密集型操作

for i in range(n):

result += math.sqrt(i) * math.sin(i)

time.sleep(0.1)

return result

# 创建分析器实例

lprofiler = LineProfiler()

# 添加要分析的函数

lprofiler.add_function(process_data)

# 运行分析

print("开始性能分析...")

lprofiler.enable_by_count()

result = process_data(100)

lprofiler.disable_by_count()

print(f"函数结果: {result}")

# 使用with open保存结果到文件

with open('profile_results.txt', 'w', encoding='utf-8') as f:

lprofiler.print_stats(f)

print("\n=== 控制台输出分析结果 ===")

lprofiler.print_stats()

开始性能分析...

函数结果: -4.787834159955427

=== 控制台输出分析结果 ===

Timer unit: 1e-09 s

Total time: 0.100183 s

File: /tmp/ipykernel_14853/4142849225.py

Function: process_data at line 6

Line # Hits Time Per Hit % Time Line Contents

==============================================================

6 def process_data(n):

7 """模拟一个需要性能分析的函数"""

8 1 388.0 388.0 0.0 result = 0

9

10 # 模拟一些计算密集型操作

11 101 16515.0 163.5 0.0 for i in range(n):

12 100 51063.0 510.6 0.1 result += math.sqrt(i) * math.sin(i)

13

14 1 100114931.0 1e+08 99.9 time.sleep(0.1)

15 1 367.0 367.0 0.0 return result

1.2 进阶使用

内存与运行时间综合分析

以下代码展示了一种整合内存与运行时间的性能分析示例,通过memory_profiler库监控内存消耗,借助LineProfiler分析代码执行时间。

from line_profiler import LineProfiler

# pip install memory_profiler

from memory_profiler import profile as memory_profile

class DataProcessor:

# 监控该函数的内存运行情况

@memory_profile

def process_large_dataset(self, data):

return list(map(self.transform_item, data))

def transform_item(self, item):

return dict(x=item** 2, y=item * 3)

processor = DataProcessor()

line_profiler = LineProfiler()

# 该函数被memory_profile装饰器包装,需要直接访问其原始未包装版本

line_profiler.add_function(processor.process_large_dataset.__wrapped__)

line_profiler.add_function(processor.transform_item)

test_data = list(range(100000))

line_profiler.enable()

result = processor.process_large_dataset(test_data)

line_profiler.disable()

line_profiler.print_stats()

Line # Mem usage Increment Occurrences Line Contents

=============================================================

5 54.4 MiB 54.4 MiB 1 @memory_profile

6 def process_large_dataset(self, data):

7 85.1 MiB 30.7 MiB 1 return list(map(self.transform_item, data))

Timer unit: 1e-07 s

Total time: 0.21659 s

File: /tmp/ipykernel_14853/4261432908.py

Function: process_large_dataset at line 5

Line # Hits Time Per Hit % Time Line Contents

==============================================================

5 @memory_profile

6 def process_large_dataset(self, data):

7 1 2165899.0 2.17e+06 100.0 return list(map(self.transform_item, data))

Total time: 0.0929938 s

File: /tmp/ipykernel_14853/4261432908.py

Function: transform_item at line 9

Line # Hits Time Per Hit % Time Line Contents

==============================================================

9 def transform_item(self, item):

10 100000 929938.0 9.3 100.0 return dict(x=item** 2, y=item * 3)

函数在多线程中的执行

line_profiler本身不区分线程,它会记录所有线程中被包装函数的执行情况。这意味着如果在多个线程中运行同一个被分析的函数,分析结果会将所有线程中该函数的执行数据汇总在一起。

from line_profiler import LineProfiler

import concurrent.futures

import time

# 要分析的函数

def process_data(data):

result = []

# 模拟一些处理操作

for i in range(data):

result.append(i ** 2)

time.sleep(0.001) # 模拟耗时操作

return sum(result)

lprofiler = LineProfiler()

lp_wrapper = lprofiler(process_data)

data_list = [100, 200, 150, 300, 250]

# 使用线程池并行处理

with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:

# 提交任务时使用被包装的函数

futures = [executor.submit(lp_wrapper, data) for data in data_list]

results = [f.result() for f in futures]

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/21 7:26:56

计算机Java毕设实战-基于springboot的汽车租赁买卖管理系统的设计与实现入库录入、租赁登记、租赁状态查询【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/13 5:20:21

华为OD机考双机位C卷 - 编程能力提升计划 (Java Python JS C/C++ GO )

最新华为上机考试 真题目录:点击查看目录 华为OD面试真题精选:点击立即查看 华为OD机考双机位C卷 题目描述 为了提升软件编码能力,小王制定了刷题计划,他选了题库中的n道题,编号从0到n-1,并计划在m天内按照题目编号顺序刷完所有的题目(注意,小王不能用多天完成同一…

作者头像 李华
网站建设 2026/3/20 7:41:50

ORACLE检查并创建表空间和表分区

为确保系统在高并发、大数据量环境下的稳定高效运行&#xff0c;要求建立完善的表空间与表分区管理机制&#xff0c;具体包括&#xff1a;定期检查表空间使用率&#xff0c;及时发现并处理空间不足风险&#xff1b;建立分区自动创建与维护流程&#xff0c;防止因分区缺失导致的…

作者头像 李华
网站建设 2026/3/14 12:21:21

港媒盛赞“香港媳妇”徐冬冬!婚照惊艳全网,港圈作品圈粉无数

12月18日&#xff0c;徐冬冬与尹子维的婚纱照强势空降热搜&#xff0c;甜酷兼具的造型让网友直呼美貌惊艳&#xff0c;气质独一份。从戏里媚骨天成的“大嫂”到戏外被港媒追捧的“香港媳妇”&#xff0c;这位东北大妞不仅用八年分合的爱情故事打动人心&#xff0c;更在港娱圈深…

作者头像 李华
网站建设 2026/3/22 16:28:05

Redis高级特性与生产环境部署

Redis高级特性与生产环境部署实践一、Redis核心数据类型深度解析1.1 哈希&#xff08;Hash&#xff09;类型详解1.1.1 哈希数据结构# 哈希结构示意图 key: "user:1001" value: {"name": "张三","age": 25,"city": "北京…

作者头像 李华
网站建设 2026/3/16 21:33:16

java计算机毕业设计网咖会员管理系统 电竞馆会员计费与点餐一体化平台 网吧会员上机充值及订单管理系统

计算机毕业设计网咖会员管理系统67kvh9&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。疫情后电竞消费井喷&#xff0c;传统网吧前台手工登记、纸质充值券、Excel对账的模式已无法…

作者头像 李华