当前课程知识点:软件工程 > 第2章 编写高质量代码 > 2.5 代码性能分析 > 讲课视频
效率是程序员之间永恒的话题
能够编写更快更好的代码
是我们每一个程序员不懈的追求
但实际做起来
效率优化
并不是一件很容易的事
它往往涉及到多方面的因素
在这一节课
我们将结合一个实例
介绍有关代码性能分析
和优化方面的内容
代码优化能够让程序运行的更快
它是指在不改变
程序运行结果的情况下
使得程序的运行效率更高
根据二八原则
实现程序的重构 优化 扩展
以及文档相关的事情
通常是要消耗80%的工作量
程序的性能一般涉及到
时间和空间两个方面
优化也就包括减少代码的体积
和提高代码的运行效率
这两项内容
代码性能优化
必须要在满足正确性
可靠性 健壮性 可读性
这些质量因素的前提下进行
在优化的时候
要注意从程序的全局效率
来考虑
不能单纯考虑局部
而且要先找出
限制效率的瓶颈
然后再对症下药
由于数据结构和算法
对程序效率起到关键的作用
因此优化的首要点是
数据结构和算法
其次才是执行代码
需要说明的是
时间效率和空间效率
往往是相互对立的
优化的时候
应当分析哪一个因素更重要
然后再作出适当的折中
程序性能应该在编程一开始
就要考虑进去
不要期待通过事后的调整
来进行提升
另外任何优化
都不能破坏代码的正确性
一般来说
代码优化过程包括以下步骤
首先要证明代码的性能
确实需要提升
然后要找出优化的关键部分
再通过测试
找出影响代码性能的瓶颈所在
对代码进行优化
最后还要对优化后的代码
再进行测试
以确定优化的效果
这里需要强调的是
测试数据的选择
必须能够代表实际的使用情况
另外永远不要在没有执行
前后性能评估的情况下
尝试对代码进行优化
现在我们通过一个程序示例
来详细说明代码优化的
步骤和方法
这个程序要求读入一个
文本文件
统计在该文件中
每个英文单词出现的频率
并输出频率最高的100个单词
其中单词指的是连续的
若干个小写英文字母
在这里我们给出了
几个单词的示例
像最后这一行
它是被逗号 句号 空格
分割成了四个单词
这是用Python实现的一个
程序代码例子
我们先介绍一下
它的基本结构
文件头部是关于
这个程序文件的注释
包括代码格式
作者信息等
最后两行是主程序的入口
相当于C++的main函数
文件头的下面是程序中
需要的各种包
这一部分是实现程序
主要功能的分词函数
现在我们来分析一下
分词函数中可能影响
性能的部分
第一是文件读入部分
文件的IO通常是速度比较慢的
第二是分割单词部分
这里使用了正则表达式来实现
第三是词频统计部分
我们用词典类型进行判重操作
最后是排序部分
这里使用了Python内置的
排序算法来实现
那么你认为
影响整个程序性能的
主要是哪一部分呢
是IO 单词分割 词频统计
还是排序部分
显然瞎猜是不行的
我们需要使用性能分析工具
来获得实际的测试数据
再来确定真正的性能瓶颈
Profile是Python语言内置的
性能分析工具
它能够有效地描述
程序运行时的性能状况
提供各种统计数据
帮助程序员找出程序的
性能瓶颈
Profile的使用非常简单
像图上给出的这个
求阶乘函数的例子
先在文件中引入Profile模块
再以程序的入口函数名为参数
来调用Profile.run这个函数
接下来就可以使用Profile
进行性能分析
这里显示的是Profile
对刚才那个阶乘函数
生成的测试结果报告
上面是程序的输出结果
这里呢是程序的执行时间
由于这个程序非常小
所以几乎没有什么耗时
下面是详细的函数性能
数据报表
它包括了函数被调用的次数
总的运行时间
运行一次的平均时间
以及函数所在的文件名
行号和函数名等
像这里显示了print这个函数
被调用了10次
以及其他的一些运行时间等
需要说明的是
tottime是除去了函数中
调用其他函数的总运行时间
cumtime是包括了
调用其他函数的总运行时间
二者是有区别的
现在我们来看一下
前面的词频统计程序的
性能数据报表
程序的总耗时是4.562秒
其中keys函数
被执行了50万次
耗时1.141秒
输出几乎没有耗时
输入耗时0.094秒
排序函数的耗时是1.203秒
分词函数耗时0.312秒
这些测试数据告诉我们
keys函数和排序函数的耗时
是比较多的
但是排序函数
是Python内置的函数
不容易进行优化
因此我们决定优化keys函数
这里需要说明的是
虽然性能分析工具
指明了程序各个部分的耗时
但并不意味着我们改进效率
一定要优先改进耗时
最多的部分
虽然改进耗时最多的部分
往往效果是明显的
但有的时候可能很难改进
在实际的项目中
我们需要在改进得到的效果
和改进要投入的精力之间
进行平衡
来优先完成有能力做
而且效果又比较明显的
性能优化
那么我们先分析一下
为什么keys函数的
调用复杂度这么高
经过查阅资料
我们发现keys函数每调用一次
系统就会生成一个新的
词典迭代器
如果这个过程重复50万次
那么效率是不可以想象的
所以我们提出了
一种优化的方案
就是使用in操作符
直接代替keys
然后这样就可以避免
每一次生成新的迭代器
这是改进后的程序代码
这部分就是修改的代码
我们对修改后的代码
再进行测试
那么测试结果发现
程序的总的执行时间
降低到了2.250秒
经过简单的优化
我们把程序的执行时间
从之前的4.562秒
降低到了2.250秒
差不多缩短了一半儿
这个效果还是比较明显的
这个例子告诉我们
性能优化的关键是
如何发现问题和解决问题
在这个过程中
有效的测试是不可缺少的
我们通过测试找出真正的瓶颈
并且分析优化的结果
需要注意的是
一定要避免不必要的优化
和不成熟的优化
因为不成熟的优化
有可能引来错误
最后我们针对Python代码的
性能优化
给出一些建议
仅供大家参考
首先是改进算法
并选择合适的数据结构
算法的好坏对程序的性能
是非常关键的
因为首先要改进算法的效率
在这里我们列出了算法的
时间复杂度的一个排序
想必大家在算法课程的学习中
已经有了基本的了解
对于成员的查找访问等操作
字典要比列表更快
因为Python字典中
使用了哈希表
它的查找复杂度是O(1)
而列表实际上就是一个数组
查找需要遍历整个列表
复杂度是O(n)
所以在需要多数据成员
进行频繁的查找
或者访问的时候
使用字典更好一些
集合的并 交 差的操作
比列表的迭代要快
涉及到求列表的
并 交 差问题
可以转化为集合来操作
循环优化的基本原则
是尽量减少循环中的计算量
有多重循环的时候
尽量将内层的计算提到上一层
对于字符串的优化
由于Python的字符串对象
是不可改变的
字符串的连接尽量使用
join函数
当对字符串可以使用
正则表达式或者内置函数
处理时 选择内置函数
列表解析要比在循环中
重新建一个新的列表
更为高效
因此可以利用这个特性
来提高代码的运行效率
关于代码性能优化的内容
就介绍到这里
希望大家课下
结合具体的实例
进一步地理解和实践
-1.1 软件无处不在
--讲课视频
-1.2 软件的本质特性
--讲授视频
-1.3 软件工程的产生与发展
--讲授视频
-1.4 软件工程的基本概念
--讲授视频
-1.5 软件质量实现
--讲授视频
-1.6 业界人士谈软件工程
-测验题--作业
-讨论题
--讨论题
-作业题
--第一张 作业题
-2.1 编程过程与规范
--讲课视频
-2.2 良好的编程实践
--讲课视频
-2.3 Python集成开发环境
--讲课视频
-2.4 代码静态检查
--讲课视频
-2.5 代码性能分析
--讲课视频
-2.6 结对编程实践
--讲课视频
-2.7 刘贺谈软件工程
--讲课视频
--讨论
-测验题--作业
-作业题
--第二章 作业题
-3.1 单元测试概述
--讲课视频
-3.2 黑盒测试方法
--黑盒测试方法
-3.3 白盒测试方法
--基本概念
--代码覆盖标准
--基本路径测试
-3.4 单元测试工具
--单元测试工具
--html
-测验题--作业
-作业题
--第三章 作业题
--作业题附件
-4.1 软件过程
--讲课视频
-4.2 软件过程模型
--讲课视频
-4.3 敏捷开发过程
--讲课视频
-4.4 微软公司开发过程
--邹欣经理自我介绍
--微软开发过程之一
--微软开发过程之二
-测验题--作业
-5.1 团队组织与管理
--讲课视频
-5.2 项目沟通管理
--讲课视频
-5.3 软件项目计划
--讲课视频
-5.4 软件项目估算
--讲课视频
-测验题--作业
-讨论题
--讨论
-6.1 敏捷开发之Scrum
-- 敏捷开发之Scrum
--html
-6.2 用户故事与估算
--讲课视频
-6.3 团队协作工具Tower
-6.4 配置管理
--讲课视频
-6.5 配置管理工具Git
--讲课视频
-测验题--作业
-作业题--作业
-7.1 需求工程师
--讲课视频
-7.2 需求定义
--讲课视频
-7.3 需求的类型
--讲课视频
--讲课视频(2)
-7.4 需求工程过程
--讲课视频
-7.5 需求的主要来源
--讲课视频
-7.6 需求获取技术
--讲课视频
--讲课视频二
--讲课视频三
-7.7 撰写需求文档
--讲课视频
-测验题--作业
-讨论题
--讨论
-8.1 用例建模概念
--讲课视频
-8.2 用例建模过程
--讲课视频
-8.3 用例建模精讲
--讲课视频
-8.4 建模工具介绍
--讲课视频
-8.5 微信抢票应用案例
--讲课视频
-测验题--作业
-讨论题
--讨论
-9.1 面向对象分析
--讲课视频
-9.2 CRC卡片分拣法
--讲课视频-1
--讲课视频-2
-9.3 面向对象设计
--讲课视频-1
--讲课视频-2
-9.4 类图建模
--讲课视频-1
--讲课视频-2
-第9章 面向对象分析与设计--测验题
-讨论题
--讨论
-10.1 顺序图概念
--讲课视频
-10.2 顺序图建模
--讲课视频
-10.3 顺序图风格
--讲义视频
-10.4 状态建模
--讲课视频
-10.5 状态图
--讲课视频
-10.6 状态图精讲
--讲义视频
-测验题--作业
-讨论题
--讨论
-11.1 软件体系结构概念
--讲授视频
-11.2 软件设计原则
--讲授视频
-11.3 软件体系结构风格(一)
--讲授视频
-11.4 软件体系结构风格(二)
--讲授视频
-11.5 软件体系结构风格(三)
--讲授视频
-11.6 软件设计过程
--讲授视频
-11.7 Web系统架构设计
--讲授视频
-11.8 数据库选择策略
--讲授视频
-测验题--作业
-作业题
--html
--html
--html
-作业题--作业
-12.1 交互设计概述
--讲授视频
-12.2 交互设计目标
--讲授视频
-12.3 GUI设计原则
--讲课视频
-12.4 KLM效率模型
--Video
-12.5 Fitts定律
--讲授视频
-12.6 交互设计过程
--讲授视频
-测验题--作业
-13.1 软件测试概念
--讲课视频
-13.2 软件测试类型
--讲课视频
-13.3 软件功能测试
--讲课视频
-13.4 软件性能测试
--讲课视频
-测验题--作业
-14.1 软件部署与交付
--讲课视频
-14.2 软件演化与维护
--讲课视频
-测验题--作业
-第一部分:基础知识
-第二部分:编程与测试(选做)