- 内容提要
- 前言
- 作者简介
- 封面简介
- 第1章 理解高性能 Python
- 第2章 通过性能分析找到瓶颈
- 2.1 高效地分析性能
- 2.2 Julia 集合的介绍
- 2.3 计算完整的 Julia 集合
- 2.4 计时的简单方法——打印和修饰
- 2.5 用 UNIX 的 time 命令进行简单的计时
- 2.6 使用 cProfile 模块
- 2.7 用 runsnakerun 对 cProfile 的输出进行可视化
- 2.8 用 line_profiler 进行逐行分析
- 2.9 用 memory_profiler 诊断内存的用量
- 2.10 用 heapy 调查堆上的对象
- 2.11 用 dowser 实时画出变量的实例
- 2.12 用 dis 模块检查 CPython 字节码
- 2.13 在优化期间进行单元测试保持代码的正确性
- 2.14 确保性能分析成功的策略
- 2.15 小结
- 第3章 列表和元组
- 第4章 字典和集合
- 第5章 迭代器和生成器
- 第6章 矩阵和矢量计算
- 第7章 编译成 C
- 第8章 并发
- 第9章 multiprocessing 模块
- 第10章 集群和工作队列
- 第11章 使用更少的 RAM
- 第12章 现场教训
2.1 高效地分析性能
性能分析的首要目标是对受测系统进行测试来发现哪里太慢(或占用太多RAM,或导致太多磁盘I/O或网络I/O)。性能分析一般会导致额外的性能开销(一般会慢10到100倍),但你依然希望你的代码尽可能像是在真正的环境中一样运行。所以要以测试用例的方式将你需要测试的那部分系统独立出来。最好这个测试用例已经使用了一套自己的模块。
本章介绍的第一个基本技术包括IPython的%timeit魔法函数,time.time(),以及一个计时修饰器。你可以使用这些技术来了解语句和函数的行为。
然后我们会学习cProfile(2.6节),告诉你如何使用这个内建工具来了解代码中哪些函数耗时最长。这将让你站在高处俯瞰你的问题,使你能够将注意力集中到关键函数上。
接下来,我们会去看line_profiler(2.8节),这个工具能够对你选定的函数进行逐行分析。其结果将包含每行被调用的次数以及每行花费的时间百分比。这恰能让你知道是哪里跑得慢以及为什么。
有了line_profiler的结果,你就有了足够的信息去使用编译器(第7章)。
在第6章(例6-8),你将学到如何使用perf stat命令来了解最终执行于CPU上的指令的个数以及CPU缓存的利用率。这让你能够进一步调优矩阵操作。读完本章后你应该去看看那个例子。
line_profiler之后,我们会演示heapy(2.10节),它可以追踪Python内存中所有的对象——这对于消灭奇怪的内存泄漏特别有用。如果你的系统需要持续运行,那么你会对dowser(2.11节)感兴趣,它让你能够通过一个Web浏览器界面审查一个持续运行的进程中的实时对象。
为了帮助你了解为什么你的RAM占用特别高,我们会给你演示memory_profiler(2.9节)。它能以图的形式展示RAM的使用情况随时间的变化,这样你就可以向你的同事们解释为什么某个函数占用了比预期更多的RAM。
备忘
无论你用什么方法分析代码性能,都必须记得用足够的单元测试覆盖你的代码。单元测试能帮助你避免愚蠢的错误并让你的结果可重现。没有单元测试风险极大。
在编译或重写你的算法之前始终进行性能分析。你需要证据来决定最有效的优化手段。
最后,我们还会给你介绍CPython中的Python字节码(2.12节),这样你就能够了解在其台面下发生了什么。具体来说,了解基于栈的Python虚拟机如何运行将帮助你明白为什么某个编程风格会跑得比别人慢。
在结束本章之前,我们会回顾如何在性能分析中集成单元测试(2.13节),让代码跑得更有效的同时维持正确。
最后我们将讨论性能分析的策略(2.14节),这样你就能够可靠地分析你的代码并收集正确的数据来验证你的假设。在这里,你将了解到动态CPU频率以及TurboBoost等特性能够如何歪曲你的性能分析结果以及如何禁用这些功能。
为了讲解所有这些步骤,我们需要以一个便于分析的函数为例。下一节我们介绍Julia集合。这是一个对RAM有一点饥渴的CPU密集型函数,而且它还具有非线性的行为(这样我们就无法轻易预测其结果),这意味着我们需要在运行时分析其性能而没法进行线下调查。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论