读程序员的制胜技笔记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语句中的逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/125958.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【前端】Jquery UI +PHP 实现表格拖动排序

目的&#xff1a;使用jquery ui库实现对表格拖拽排序&#xff0c;并且把排序保存到数据库中 效果如下 一、准备工作&#xff1a; 1、下载jquery ui库&#xff0c;可以直接引用线上路径 <link rel"stylesheet" href"https://code.jquery.com/ui/1.12.1/them…

C++中的函数重载:多功能而强大的特性

引言 函数重载是C编程语言中的一项强大特性&#xff0c;它允许在同一个作用域内定义多个同名函数&#xff0c;但这些函数在参数类型、个数或顺序上有所不同。本文将深入探讨函数重载的用法&#xff0c;以及它的优势和应用场景。 正文 在C中&#xff0c;函数重载是一项非常有…

【C#】Mapster对象映射的使用

系列文章 【C#】编号生成器&#xff08;定义单号规则、固定字符、流水号、业务单号&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129129787 【C#】日期范围生成器&#xff08;开始日期、结束日期&#xff09; 本文链接&#xff1a;h…

【Java】SPI在Java中的实现与应用

一、SPI的概念 1.1、什么是API&#xff1f; API在我们日常开发工作中是比较直观可以看到的&#xff0c;比如在 Spring 项目中&#xff0c;我们通常习惯在写 service 层代码前&#xff0c;添加一个接口层&#xff0c;对于 service 的调用一般也都是基于接口操作&#xff0c;通…

【Git】如何安装git,项目中使用git上传到远程仓库,使用git中对多人使用出现的版本问题的解决

前言&#xff1a; 一&#xff0c;Git的介绍&#xff0c;安装&#xff0c;与SVN的对比 1.1Git的介绍 Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控…

民生画派创始人张龙(天驰)作品

简介 张龙&#xff08;天驰&#xff09; 中国民生画派创始人 首届“陆俨少奖”金奖得主 人民大学巨幅主题创作高级研修班导师 中央美院客座教授 神舟十二号载人飞船遨游太空搭载作品创作者 被评为2021、2022年年度最具收藏价值艺术家 中国美术家协会会员 中国美术家协…

【数据结构】单链表OJ题(二)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、分割链表二、回文链表三、相交链表四、环形链表 I五、环形链表 II 六、链表的深度拷…

17 Linux 中断

一、Linux 中断简介 1. Linux 中断 API 函数 ① 中断号 每个中断都有一个中断号&#xff0c;通过中断号可以区分出不同的中断。在 Linux 内核中使用一个 int 变量表示中断号。 ② request_irq 函数 在 Linux 中想要使用某个中断是需要申请的&#xff0c;request_irq 函数就是…

【python海洋专题四十四】海洋指数画法--多色渐变柱状图

【python海洋专题四十四】海洋指数画法–多色渐变柱状图

winform开发小技巧

如果我们不知道怎么在代码中new 一个控件&#xff0c;我们可以先在窗体中拉一个然后看Form1.Designer.cs 里面生成的代码就是我们要的 我们会在下面看到 还有泛型的使用&#xff0c;马上更新

Termius for Mac:掌控您的云端世界,安全高效的SSH客户端

你是否曾经在Mac上苦苦寻找一个好用的SSH客户端&#xff0c;让你能够远程连接到Linux服务器&#xff0c;轻松管理你的云端世界&#xff1f;现在&#xff0c;我们向你介绍一款强大而高效的SSH客户端——Termius。 Termius是一款专为Mac用户设计的SSH客户端&#xff0c;它提供了…

JavaScript从入门到精通系列第三十二篇:详解正则表达式语法(一)

文章目录 一&#xff1a;正则表达式 1&#xff1a;量词设置次数 2&#xff1a;检查字符串以什么开头 3&#xff1a;检查字符串以什么结尾 4&#xff1a; 同时使用开头结尾 5&#xff1a;同值开头同值结尾 二&#xff1a;练习 1&#xff1a;检查是否是一个手机号 大神链…

『MySQL快速上手』-⑤-数据类型

文章目录 1.数据类型有哪些2.数值类型2.1 tinyint 类型2.2 bit 类型2.3 小数类型2.3.1 float2.3.2 decimal3.字符串类型3.1 char3.2 varchar3.2 char 和 varchar 比较4.日期和时间类型5.enum和set1.数据类型有哪些 MySQL支持多种数据类型,这些数据类型可用于定义表中的列,以…

Selenium关于内容信息的获取读取

在进行自然语言处理、文本分类聚类、推荐系统、舆情分析等研究中,通常需要使用新浪微博的数据作为语料,这篇文章主要介绍如果使用Python和Selenium爬取自定义新浪微博语料。因为网上完整的语料比较少,而使用Selenium方法有点简单、速度也比较慢,但方法可行,同时能够输入验…

【Unity ShaderGraph】| 如何快速制作一个炫酷的 全息投影效果

前言 【Unity ShaderGraph】| 如何快速制作一个炫酷的 全息投影效果一、效果展示二、 全息投影效果 前言 本文将使用ShaderGraph制作一个 炫酷的 全息投影效果 &#xff0c;可以直接拿到项目中使用。对ShaderGraph还不了解的小伙伴可以参考这篇文章&#xff1a;【Unity Shader…

三国志14信息查询小程序(历史武将信息一览)制作更新过程06-复现小程序

0&#xff0c;所需文件 所需全部文件下载 文件总览&#xff1a; 代码&#xff1a; 数据库&#xff1a; 1&#xff0c;前期准备 假定你已经看过前面的文章&#xff0c;并完成了下列准备&#xff1a; &#xff08;1&#xff09;一台有公网IP的云服务器&#xff0c;服务器上…

Oracle 三种分页方法(rownum、offset和fetch、row_number() over())

Oracle的三种分页指的是在进行分页查询时&#xff0c;使用三种不同的方式来实现分页效果&#xff0c;分别是使用rownum、使用offset和fetch、使用row_number() over() 1、使用rownum rownum是oracle中一个伪劣&#xff0c;它用于表示返回的行的序号。使用rownum进行分页查询的方…

数据结构之单链表基本操作

&#x1f937;‍♀️&#x1f937;‍♀️&#x1f937;‍♀️ 今天给大家分享的是单链表的基本操作。 清风的个人主页 &#x1f389;欢迎&#x1f44d;点赞✍评论❤️收藏 &#x1f61b;&#x1f61b;&#x1f61b;希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位…

大数据毕业设计选题推荐-农作物观测站综合监控平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

计算当月工作日时间进度

目录 1.按一个月平均算 2.除去星期六星期天算 3.自定义节假日算 1.按一个月平均算 // 获取当前时间 const now new Date(); // 获取当前年份和月份 const currentYear now.getFullYear(); const currentMonth now.getMonth() 1; // 计算当月天数 const daysInMonth ne…