工作后的读书笔记

最后更新于:2022年6月25日 下午

书依然在看,笔记不是很想写了

optim_cpp

为何软件经常很慢

  • 硬件过去几十年飞速发展,但是现在到达了物理上的瓶颈
  • 软件速度的递减速度快于硬件的增长

解决办法

  • 设计多进程多线程
  • 提高代码效率

优化的代价

以前,硬件性能还不行的时候,推荐不要把单次执行的代码当作子模块来使用,但是现在工程越来越大,这种做法会带来很多的问题,如何均衡性能和可读性可维护性呢?

  • 隔离核心代码
  • 对具体代码的某些部分进行优化
  • 如何在不明显降低性能的情况下处理异常:边界检验,野指针等
  • 要了解哪些构造函数是开销

库的选择

相对耗时的组件

  • 文件 IO
  • 音频图像处理
  • 内存和字符串处理
  • 数学函数
  • 加密解密,数据处理

然而标准库并非对所有这些耗时的模块都很好的优化了,因此可以根据需求调用不同的库。

C++ 的缺点

  • 跨平台是基于语言标准以及主流平台的支持。C++ 可以直接操作硬件(当然此时就做不到跨平台了)所以基本上此时我们就需要模块分开
  • 开发时间:可用模块化和可复用的类显著降低
  • 安全:边界检测和野指针等通常是黑客攻击的地方。有必要给出一个准则来处理安全问题,但这也可能先来效率的下降。

安全详解

比如有一个数组 \(a[3]\) 和数字 \(b\),那是否我访问 \(a[3] = x\) 就能改变 b 的值呢?

  • 边界检测:用 vector(但是这会影响效率)
  • 字符串:String 也是低效的(更有效的做法是用内存池,不懂不懂不懂)
  • (整数)溢出:C 标准未定义有符号的溢出,因此各个编译器实现不同,当然可以加参数(-ftrapv, -Wstrict-overflow=2, -fwrapv, -fno-strict-overflow)来处理这些溢出(或者提前 check)

内存访问

  • L1 缓存: 8 - 64 k
  • L2 缓存:256k - 2 M
  • L3 缓存:1G 左右

以上数值范围随着时间增加,但是不会很离谱的增加,mac 可通过 sysctl -a | grep "size" 查看

网络和硬件问题

这些都是无法保证的,因此需要考虑失效后怎么处理,网络有指数规避策略,硬件可以申请重试。

流水线

现代计算机都是流水线设计,即使是单核的 CPU,只要执行的指令无依赖,都可以流水线完成

我以前以为 a = ++ba = b++ 快,因为前者不需要再生成一个变量,但是其实后者快,因为后者可被流水线优化(实测发现速度没差别)

选择优的算法

反对过度优化:如果一个普通的算法就能快速解决问题,就不要用高级和复杂的算法

Pragmatic Programmer

发现问题,不急不忙的分析的品质,不会?没关系,学,问别人,是成为一个程序员的自我修养

实事求是的哲学

在所有的弱点中,最大的弱点就是害怕暴露弱点。 J.B.Bossuet 1709

  • 备份你的代码
  • 遇到问题:提供各种选择,而非找蹩脚的理由(想想别人听你的理由会怎么想)
  • 保持代码,文档的优雅
  • 让你的用户参与权衡,知道你自己想说 什么

实事求是的途径

不要重复

DRY(Don't Repeat Yourself):不要重复自己

原则:系统中的每一项只是都必须具有单一,无歧义,权威的表示

代码中的文档:糟糕的代码才需要注释,注释会不可避免地变得过时

减少重复,但是不影响使用缓存

1
2
3
4
5
6
class Line {
public:
Point start;
Point end;
double length;
};

这上面就重复了,但是有时候 length 数据很难得到,这个时候为了效率我们就可能会需要缓存,即

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Line {
bool changed;
double length;
Point start;
Point end;
public:
void setStart(Point p) { start = p; changed = true;}
void setEnd(Point P) { end = p; changed = true;}
Point getStart(void) { return start;}
Point getEnd(void) {return end;}
double getLength() {
if (changed) {
length = start.distanceTo(end);
changed = false;
}
return changed;
}
}

但是上述做法只适用于单线程

写复用性更好的代码

正交性

  • 各个模块相互不影响,解耦
  • 提高生产力
  • 降低风险
  • 更利于拓展
  • 文档也可以做成满足正交性

实事求是的偏执

  • 你不可能写出完美的软件
  • 需求之坑
  • 文明通过增加文明不假思索就能完成的重要操作的数量而取得进步

文档

  • 内部版和外部版

  • 为自己的作品署名

  • 不要重复,因此自解释的代码不需要注释

性能之巅

技术工程师如何发展

  • 确定自己喜欢软件技术,愿意在技术上持久发展
  • 日日新,又日新
  • 逐级攀登软件技术的三级阶梯:编码,调试,调优(张银奎)
  • 相信量变引起质变

未知的未知:我不知道我不知道

设计

  • 如果一款小工具需要费力的说明,那很可能这是一个失败的设计
  • 性能这块领域是,“你知道的越多,你不知道的也就越多”。这和学习系统是一样的原理:你了解的越多,你就能意识到未知的未知就越多,然后这些未知的未知会变成你可以去查看的已知的未知
  • 性能指标不是免费的,在某些时候,会消耗一些 CPU 周期来手机和保存指标信息。对目标的性能会有负面影响,这种影响被称为观察者效应

ProGit

  • stream of snapshots 不太现实吧!(要相信压缩的技术)
  • git checkout