代码调优法则

2024-06-23 15:32
文章标签 代码 法则 调优

本文主要是介绍代码调优法则,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《编程珠玑》第二版快看完了,发现附录D作者记录了一些代码调优法则,本人总结了一下,发出来给大家分享分享,顺便自己也做个笔记。(注:这篇文章是用那个markdown写的,第一次用markdown发布博客,有点小激动。)

代码调优法则

空间换时间法则

  • 修改数据结构。为了减少数据上的常见运算所需要的时间,我们通常可以在数据结构中增加额外的信息,或者修改数据结构中的信息使之更易访问。
    例如:std::vector中的empty和size成员函数之类的。
  • 存储预先计算好的结果。对于开销较大的函数,可以只计算一次,然后将计算结果存储起来以减少开销。以后再需要该函数时,可以直接查表而不需要重新计算。
    例如:读取文件中的数据时不用每次都去读取,第一次读取出来时保存数据即可。
  • 高速缓存。最经常访问的数据,其访问开销应该是最小的。
  • 懒惰求值。除非需要,否则不对任何一项求值。这一策略可以避免对不必要的项求值。
    例如:类模板中只对使用到的成员函数实例化。

时间换空间法则

  • 堆积。密集存储表示可以通过增加存储和检索数据所需的时间来减少存储开销。
    例如:使用数组来表示稀疏矩阵。
  • 解释程序。使用解释程序通常可以减少表示程序所需的空间,在解释程序中常见的操作序列以一种紧凑的方式表示。

循环法则

  • 将代码移出循环。与其在循环的每次迭代时都执行一次某种计算,不如将其移到循环体外,只计算一次。
  • 合并测试条件。高效的内循环应该包含尽量少的测试条件,最好只有一个。因此,程序员应尽量用一些退出条件来模拟循环的其他退出条件。
    例如:哨兵是该法则的常见应用,在数据结构的边界上放一个哨兵以减少测试是否已搜索结束的开销。
  • 循环展开。循环展开可以减少修改循环下标的开销,对于避免管道延迟、减少分支以及增加指令级的并行性也都很有帮助。
  • 删除赋值。如果内循环中很多开销来自普通的赋值,通常可以通过重复代码并修改变量的使用来删除这些赋值。
  • 消除无条件分支。快速的循环中不应该包含无条件分支。通过“旋转”循环,在底部加上一个条件分支,能够消除循环结束处的无条件分支。该操作通常由编译器优化完成。
  • 循环合并。如果两个相邻的循环作用在同一组元素上,那么可以合并其运算部分,仅使用一组循环控制操作。

逻辑法则

  • 利用等价的代数表达式。如果逻辑表达式的求值开销太大,就将其替换为开销较小的等价代数表达式。
  • 短路单调函数。一旦找到了循环的目的就可以退出循环了。
  • 对测试条件重新排序。在组织逻辑测试的时候,应该将低开销的、经常成功的测试放在高开销的、很少成功的测试前面。
  • 预先计算逻辑函数。在比较小的有限域上,可以用查表来取代逻辑函数。
  • 消除布尔变量。我们可以用if-else语句来取代对布尔变量的赋值,从而取消程序中的布尔变量。

过程法则

  • 打破函数层次。对于(非递归地)调用自身的函数,通常可以通过将其改写为内联版本并固定传入的变量来缩短其运行时间。
  • 高效处理常见情况。应该使函数能正确处理所有情况,并能搞笑处理常见情况。
  • 协同程序。通常,使用协同例程能够将多趟算法转换为单趟算法。
  • 递归函数转换。递归函数的运行时间往往可以通过下面的转换来缩短:
    1.将递归重写为迭代,通过使用一个显式的程序栈将递归转化为迭代(如果函数仅包含一个对其自身的递归调用,那么就没有必要将返回地址存储在栈中);
    2.如果函数的最后一步是递归调用其自身,那么使用一个到其第一条语句的分支来替换该调用,这通常称为消除尾递归。该操作通常由编译器优化完成。
    3.解决小的子问题时,使用辅助过程通常比把问题的规模变为0或1更有效。例如:快速排序递归的规模小于50的时候不再递归,对整体数组使用一次插入排序比继续递归的速度更快。
  • 并行性。在底层硬件条件下,构建的程序应该尽可能多地挖掘并行性。

表达式法则

  • 编译时初始化。在程序执行之前,应该对使用到的变量初始化。
  • 利用等价的代数表达式。如果表达式的求值开销太大,就将其替换为开销较小的等价代数表达式。
    例如:使用if语句替换%取模运算。
  • 消除公共子表达式。如果两次对同一个表达式求值时,其所有变量都没有任何改动,那么我们可以用下面的方法避免第二次求值:存储第一次的计算结果并用其取代第二次求值。
    例如:第二次读取文件数据前检查文件的修改日期,若没有改变,则使用第一次读取的数据即可。
  • 成对计算。如果经常需要对两个类似的表达式一起求值,那么就应该建立一个新的过程,将它们成对求值。
  • 利用计算机字的并行性。用底层计算机体系结构的全部数据路径宽度来对高开销的表达式求值。

这篇关于代码调优法则的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1087553

相关文章

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

MySQL的配置文件详解及实例代码

《MySQL的配置文件详解及实例代码》MySQL的配置文件是服务器运行的重要组成部分,用于设置服务器操作的各种参数,下面:本文主要介绍MySQL配置文件的相关资料,文中通过代码介绍的非常详细,需要... 目录前言一、配置文件结构1.[mysqld]2.[client]3.[mysql]4.[mysqldum

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

IDEA与MyEclipse代码量统计方式

《IDEA与MyEclipse代码量统计方式》文章介绍在项目中不安装第三方工具统计代码行数的方法,分别说明MyEclipse通过正则搜索(排除空行和注释)及IDEA使用Statistic插件或调整搜索... 目录项目场景MyEclipse代码量统计IDEA代码量统计总结项目场景在项目中,有时候我们需要统计

MySQL设置密码复杂度策略的完整步骤(附代码示例)

《MySQL设置密码复杂度策略的完整步骤(附代码示例)》MySQL密码策略还可能包括密码复杂度的检查,如是否要求密码包含大写字母、小写字母、数字和特殊字符等,:本文主要介绍MySQL设置密码复杂度... 目录前言1. 使用 validate_password 插件1.1 启用 validate_passwo

MySQL实现多源复制的示例代码

《MySQL实现多源复制的示例代码》MySQL的多源复制允许一个从服务器从多个主服务器复制数据,这在需要将多个数据源汇聚到一个数据库实例时非常有用,下面就来详细的介绍一下,感兴趣的可以了解一下... 目录一、多源复制原理二、多源复制配置步骤2.1 主服务器配置Master1配置Master2配置2.2 从服

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据