当前位置: 首页 > news >正文

读程序员的制胜技笔记08_死磕优化(上)

1. 过早的优化是万恶之源

1.1. 著名的计算机科学家高德纳(Donald Knuth)的一句名言

1.2. 原话是:“对于约97%的微小优化点,我们应该忽略它们:过早的优化是万恶之源。而对于剩下的关键的3%,我们则不能放弃优化的机会。”

2. 过早优化是提升自己的根源

2.1. 优化就是解决问题,过早优化创造了暂时没有发现的、假想的问题来解决,就像国际象棋选手设置棋局来挑战自己

2.2. 探索性编程是提高技能的合法途径

3. 不要过早优化的原因

3.1. 优化会增加代码的耦合性,使其更难维护

3.2. 优化也是一项投资,其回报在很大程度上取决于你能将优化结果保持多久

3.3. 如果规范发生变化,你所进行的优化可能会让你陷入一个难以摆脱的困境

3.4. 你可能试图为一个本来就不存在的问题进行优化,而使你的代码变得不那么可靠

4. 解决该解决的问题

4.1. 你需要真正理解你在优化时到底做了什么权衡,这意味着你必须把需要解决的问题了解透彻

4.2. 根据问题的性质,解决方式可以发挥的效用和实现它需要花费的时间可能有很大的不同

4.3. 基准测试(benchmarking)

4.3.1. 比较性能指标的行为

4.3.1.1. 只能给你一堆用于比较的数字
4.3.1.2. 不能告诉你代码的运行速度是快还是慢
4.3.1.3. 可以告诉你它们比其他一些代码运行得慢还是快

4.3.2. 无法帮助你确定造成性能问题的根本原因

4.3.3. 可以帮助你确定是否存在性能问题

4.3.4. 你应该常常对你的那些代码优化进行基准测试,来看看你的优化是否还有更进一步的余地

4.3.5. BenchmarkDotNet库

4.3.5.1. 可以消除因测量误差或者调用开销产生的波动
4.3.5.2. 适用于微观基准测试
4.3.5.2.1. 适用于微观基准测试
4.3.5.3. 基准测试并没有试图消除函数调用的开销或for循环本身的开销

4.3.6. Math.DivRem()函数比普通的除法和求余操作能快多少

4.3.6.1. C#
public class SampleBenchmarkSuite {[Params(1000)]  ⇽--- 避免编译器优化public int A;[Params(35)]  ⇽--- public int B;[Benchmark]  ⇽--- 用属性标记要进行基准测试的操作public int Manual() {int division = A / B;int remainder = A % B;return division + remainder;  ⇽--- 我们将值返回,这样编译器就不会丢掉计算步骤}[Benchmark]  ⇽--- public int DivRem() {int division = Math.DivRem(A, B, out int remainder);return division + remainder;  ⇽--- }
}
using System;
using System.Diagnostics;
using BenchmarkDotNet.Running;
namespace SimpleBenchmarkRunner {public class Program {public static void Main(string[] args) {BenchmarkRunner.Run<SampleBenchmarkSuite>();}}
}
4.3.6.2. Math.DivRem()的速度是分别进行除法和求余操作的两倍
4.3.6.3. 使用Stopwatch编写自己的基准测试程序
4.3.6.4. C#
private const int iterations = 1_000_000_000;
private static void runBenchmarks() {var suite = new SampleBenchmarkSuite {A = 1000,B = 35};long manualTime = runBenchmark(() => suite.Manual());long divRemTime = runBenchmark(() => suite.DivRem());reportResult("Manual", manualTime);reportResult("DivRem", divRemTime);}
private static long runBenchmark(Func<int> action) {var watch = Stopwatch.StartNew();for (int n = 0; n < iterations; n++) {action();  ⇽--- 我们在这里调用基准测试代码}watch.Stop();return watch.ElapsedMilliseconds;
}
private static void reportResult(string name, long milliseconds) {double nanoseconds = milliseconds * 1_000_000;Console.WriteLine("{0} = {1}ns / operation",name,nanoseconds / iterations);
}
4.3.6.5. DivRem函数的运行速度比除法和求余操作快,因为它被转换为需要更少周期的指令

4.4. 性能与响应性

4.4.1. 关于缓慢的一般原则

4.4.1.1. 任何需要超过100毫秒的动作都会让人感觉到延迟,而任何需要超过300毫秒的动作都被认为是缓慢的,更不要说花整整1秒的动作

4.4.2. 性能并不总是与响应性(responsiveness)有关

4.4.3. 任务是计算密集型(computationally intensive)的

4.4.3.1. 最快的计算方法是在工作完成之前不做其他事情
4.4.3.2. 与其以最快的速度进行计算,不如腾出一些计算周期来显示一个进度条,也许可以计算出估计的剩余时间,并在用户等待的时候显示一个漂亮的动画
4.4.3.3. 最后,你的代码运行速度会变慢,但结果会更成功

4.4.4. 延迟也会影响性能,而不仅仅是用户体验

4.4.4.1. 你的数据库驻留在磁盘上,而你的数据库服务器驻留在网络上,这意味着,即使你写了最快的SQL查询,并在你的数据库上定义了最快的索引,你仍然受到物理定律的约束,你不能得到任何快于1毫秒的结果

5. 迟缓的剖析

5.1. 并不是所有的性能问题都是关于速度的

5.1.1. 有些是关于响应性的

5.2. CPU是处理从RAM中读取的指令的芯片,并在一个永无止境的循环中重复执行这些指令

5.3. 时钟周期(clock cycle)

5.3.1. 简称为周期

5.4. CPU速度通常以赫兹(Hz)为单位,表示它在1秒内能处理多少个时钟周期

5.5. 有时CPU甚至可以处理比其处理速度所允许的更多指令

5.5.1. 一些指令需要一个以上的时钟周期来完成

5.5.2. 现代CPU可以在一个核心上并行处理多条指令

5.6. 每一个与代码执行速度有关的性能问题都归结为有多少条指令被执行和被执行多少次

5.7. 当你优化代码时,本质上你要做的是减少指令的执行次数,或者使用更快版本的指令

6. 从头开始

6.1. 直接在源头解决问题

6.1.1. 定位到根本问题

6.2. 减少执行指令数量第二好的方法是选择一个更快的算法

6.3. 最好的方法显然是完全删除代码

6.3.1. 删除你不需要的代码

6.3.2. 不要在代码库中保留不需要的代码

6.3.2.1. 即使不会直接降低代码的性能,也会降低开发人员的“性能”,最终降低代码的性能

6.3.3. 不要保留注释过的代码

6.3.3.1. 可以使用你最喜欢的源代码控制系统(如Git或Mercurial)的历史功能来恢复旧代码

6.4. 让代码运行速度变慢的最简单的方法之一是把它放在另一个循环里

6.4.1. 不应该把计算密集型的代码放在属性的源代码里面

6.4.2. 小心属性

6.4.2.1. 它们包含逻辑,而它们的逻辑并不简单

6.5. 面向字符串的编程

6.5.1. 选择适合的类型会比使用字符串拥有更好的性能

6.5.2. 字符串有一些微妙的方式可以被添加进你的代码里

6.5.3. 一个布尔变量来优化代码

6.5.3.1. C#
if ((string)HttpContext.Items["Bozo"] == "true") {
...
}
6.5.3.2. C#
if ((bool?)HttpContext.Items["Bozo"] == true) {
...
}
6.5.3.3. 节省存储开销和解析开销,还有助于避免你的打字错误,比如把True打成ture
6.5.3.4. 简单的错误对你来说影响不是特别大,但如果错误变成了习惯,影响就会积小成大

6.6. if语句中的布尔表达式是按照它们的书写顺序来评估

6.6.1. 可以简单地调换表达式的位置

6.6.2. 建议根据操作数类型对表达式进行排序

6.6.2.1. 1.变量
6.6.2.2. 2.字段
6.6.2.3. 3.属性
6.6.2.4. 4.方法调用

6.6.3. 逻辑符号是有优先级之说的,以确保你在优化布尔运算时不会意外地破坏if语句中的逻辑

相关文章:

读程序员的制胜技笔记08_死磕优化(上)

1. 过早的优化是万恶之源 1.1. 著名的计算机科学家高德纳(Donald Knuth)的一句名言 1.2. 原话是:“对于约97%的微小优化点,我们应该忽略它们:过早的优化是万恶之源。而对于剩下的关键的3%,我们则不能放弃优化的机会。” 2. 过早…...

【gltf-pipeline】安装gltf-pipeline 进行文件格式转换

问题 想使用gltf-pipeline进行gltf和glb格式转换。简单记录一下安装过程。 解决 1、安装Node.js Node.js下载路径:https://nodejs.org/en 建议默认设置安装。 添加系统环境变量: 测试安装是否成功: 在cmd.exe中运行: no…...

Android OpenGL ES踩坑记录

因为项目中的一个自定义绘图控件性能不行,改用OpenGL实现,也是第一次使用OpenGL,由于只是绘制2D图形,参考官方以及网上的教程,实现起来还是比较顺畅的,开发时只用了两个手机测试,运行良好&#…...

Vue3 项目完整配置

目录 一、配置简述二、创建项目1、使用包管理工具 pnpm2、新增目录 三、配置 ESLint1、添加代码2、修改 VSCode 配置 四、husky 工具配置五、暂存区 eslint 校验六、axios 配置1、安装创建2、测试 七、导入 Element Plus八、Pinia 持久化实现九、其他导入 .scss 文件需要安装 s…...

二十三种设计模式全面解析-从线程安全到创新应用:探索享元模式的进阶之路

在软件开发领域,线程安全和设计模式都是我们经常遇到的话题。线程安全保证了多线程环境下的数据一致性和可靠性,而设计模式则提供了一套经验丰富的解决方案。在前文中,我们已经了解了线程安全的处理和享元模式的基本概念。但是,如…...

Qt之qobject_cast使用

描述 qobject_cast是Qt中的一个转换函数&#xff0c;主要用于在QObject子类之间进行转换&#xff0c;实现父类指针向子类指针的转换。其语法为&#xff1a; qobject_cast<T>(object);其中&#xff0c;T表示目标类型&#xff0c;object表示要转换的QObject对象指针。 q…...

如何实现云端开发能力快速提升?【DevRun】云上开发创新实践带你实现

随着企业数字化的转型趋势&#xff0c;软件成为数字化转型的关键驱动力&#xff0c;在云计算越来越普及且作用愈发重要的今天&#xff0c;现代应用正以难以想象的速度在增长&#xff0c;同时对软件开发工具提出了新的要求。 华为云CodeArts作为一站式云上开发创新工具&#xf…...

猫头虎博主第7期赠书活动:《计算机考研精炼1000题》

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

Linux常见指令:从基础到理论

前言 目录 前言 1. find指令 拓展 2. grep指令 拓展 sort指令 uniq指令 wc指令 3. zip/unzip指令 4. tar指令 5. uname指令 拓展 6. Linux常用热键 7. 关机 8. rz指令 拓展 scp指令 9. shell命令以及运行原理 Linux常见指令是使用Linux系统时必不可少的一部分。通过掌握…...

ARM---CAN2.0B读取 汽车BMS报文

Qt CAN编程1- CAN总线整体介绍 - 简书 STM32---CAN2.0B读取新能源汽车BMS报文-CSDN博客 CAN通信标准帧和扩展帧介绍_can扩展帧-CSDN博客 【精选】详解CAN 2.0协议_can2.0-CSDN博客 QT实现CAN通信_五个板栗的技术博客_51CTO博客 linux CAN通讯基于Qt代码编写_pf_can-CSDN博客…...

第十一章 JSP开发模型

文章目录 一. 单选题&#xff08;共5题&#xff0c;50分&#xff09;二. 判断题&#xff08;共5题&#xff0c;50分&#xff09; 一. 单选题&#xff08;共5题&#xff0c;50分&#xff09; (单选题) JSPModel2开发的系统中&#xff0c;实现视图的是&#xff08;&#xff09; A…...

Java面试题-Redis-第四天(线程模型一)

目录 一、Redis为何选择单线程&#xff1f; 二、Redis真的是单线程吗&#xff1f; 三、Redis6.0为何引入多线程 四、Redis6.0引入多线程之后&#xff0c;性能的提升效果如何&#xff1f; 一、Redis为何选择单线程&#xff1f; 通常对于一个数据库来说&#xff0c;CPU通常不…...

逐次变分模态分解(Sequential Variational Mode Decomposition,SVMD)(附代码)

代码原理 逐次变分模态分解&#xff08;Sequential Variational Mode Decomposition&#xff0c;SVMD&#xff09;是一种用于信号处理和数据分析的方法。它可以将复杂的信号分解为一系列模态函数&#xff0c;每个模态函数代表了信号中的一个特定频率成分。SVMD的主要目标是提取…...

Spring Boot(一)

Spring Boot是一个开源的Java框架&#xff0c;旨在简化基于Java的应用程序的开发和部署过程。它提供了许多开箱即用的功能和工具&#xff0c;使开发者能够快速构建独立、可执行的、生产级别的应用程序。 以下是Spring Boot的一些主要特点和优势&#xff1a; 简化的配置&#x…...

秩为1的矩阵的性质

...

酷安官网下载页前端自适应源码

酷安官网下载页前端自适应源码&#xff0c;自己拿走玩玩 站长只打开看了一眼&#xff0c;感觉风格还不错&#xff0c;纯html&#xff0c;自己魔改 转载自 https://www.qnziyw.cn/wysc/qdmb/24470.html...

Docker实战

一、Docker安装 以下均以CentOS 7为例 1、安装Docker yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2、启动和校验 # 启动Docker systemctl start docker# 停止Docker systemctl stop docker# 重启 systemctl resta…...

什么是代理IP池?如何判断IP代理商的IP池是否真实优质?

代理池充当多个代理服务器的存储库&#xff0c;提供在线安全和匿名层。代理池允许用户抓取数据、访问受限制的内容以及执行其他在线任务&#xff0c;而无需担心被检测或阻止的风险。代理池为各种在线活动&#xff08;例如网页抓取、安全浏览等&#xff09;提高后勤保障。 读完…...

嵌入式养成计划-51----ARM--ARM汇编指令--内存读写指令--程序状态寄存器传输指令--软中断指令--混合编程

一百二十七、内存读写指令 通过内存读写指令可以实现向内存中写入指定数据或者读取指定内存地址的数据 127.1 单寄存器内存读写指令 将一个寄存器中的数值写入到内存&#xff0c;或者从内存中读取数据放在某一个指定寄存器中 127.1.1 指令码和功能 1. 向内存中写&#xff…...

RSA 2048位算法的主要参数N,E,P,Q,DP,DQ,Qinv,D分别是什么意思 哪个是通常所说的公钥与私钥 -安全行业基础篇5

非对称加密算法RSA 在RSA 2048位算法中&#xff0c;常见的参数N、E、P、Q、DP、DQ、Qinv和D代表以下含义&#xff1a; N&#xff08;Modulus&#xff09;&#xff1a;模数&#xff0c;是两个大素数P和Q的乘积。N的长度决定了RSA算法的安全性。 E&#xff08;Public Exponent&a…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

高防服务器价格高原因分析

高防服务器的价格较高&#xff0c;主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因&#xff1a; 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器&#xff0c;因此…...