C++核心指导原则: 哲学部分

C++ Core Guidelines 是由 Bjarne Stroustrup 与其他专家共同制定的一系列关于如何高效, 安全地使用 C++的指导原则. 在 C++ Core Guidelines 的 Philosophy(P)部分, 提供了关于编写高质量, 可维护和安全的 C++代码的高层次指导原则.

C++ Core Guidelines 整理目录

  1. 哲学部分
  2. 接口(Interface)部分
  3. 函数部分
  4. 类和类层次结构部分
  5. 枚举部分
  6. 资源管理部分
  7. 表达式和语句部分
  8. 性能部分
  9. 并发和并行
  10. 错误处理
  11. 常量和不可变性
  12. 泛型编程
  13. 源文件
  14. 命名和布局建议
  15. 标准库
  16. 其他规则

P: Philosophy

P.1: Express ideas directly in code

class Date {
  // ...
 public:
  Date(Year year, Month month, Day day);  // good
  Date(int year, int month, int day);     // bad
};

比如这个示例中, 使用 Year, MonthDay 来构造 Date 对象, 而不是使用 int 作为参数. 连续三个 int 类型的参数很容易出现传参错误而且编译器无法发现. 而使用强类型则可以最大程度上避免传参错误.

int index = -1;  // bad
for (int i = 0; i < v.size(); ++i) {
  if (v[i] == target) {
    index = i;
    break;
  }
}
auto it = std::find(begin(v), end(v), target);  // better

使用 std::find 而不是 for 循环, 可以更好的表达意图.

P.2: Write in ISO standard C++

P.3: Use libraries wherever possible

P.4: Avoid writing new code when a library can do the job

P.5: Prefer simple and direct code over clever optimizations

P.6: Do not optimize prematurely

P.7: Make interfaces precisely and strongly typed

P.8: Keep it simple

P.9: Design for correctness, efficiency, and maintainability

P.10: Use const when possible

P.11: Use constexpr when possible

P.12: Use inline functions for small, frequently called functions

P.13: Use RAII (Resource Acquisition Is Initialization) for resource management

P.14: Prefer value semantics over reference semantics when possible

P.15: Minimize dependencies between modules

P.16: Use namespaces to avoid name clashes

P.17: Avoid global variables

补充材料

Amdahl’s Law

Amdahl’s Law 是并行计算领域中用于预测通过增加处理器数量所能达到的加速比的一个公式. 它由计算机科学家 Gene Amdahl 在 1967 年提出, 主要用于评估并行计算系统的效率和性能提升极限.

Amdahl’s Law 表明, 程序的加速比受到其串行部分的限制, 即无论增加多少处理器或并行执行单元, 程序的最大加速比都受限于不能并行化的那部分工作.

$$S_{latency}(s) = \frac{1}{(1 - P) + \frac{P}{s}} $$

其中$S_{latency}$是加速比, $P$是可并行化的工作比例, 而$s$是并行处理单元的数量(比如 CPU 核心数).

根据这个定律, 如果一个程序中有一定比例的任务必须按顺序执行(不可并行化), 那么即使剩余任务可以完全并行化, 随着并行处理单元数量的增加, 整体加速效果将逐渐接近某个极限值. 这是因为随着并行处理单元的增加, 那些无法被并行化的任务所占的比例相对于整个执行时间变得越来越大. 这个给我们的指导就是一定要抓住主要矛盾, 抓住耗时最大的地方进行优化.

举例介绍

假定一个程序员花了不小代价将一部分代码提高了 10 倍(s = 10), 这个很厉害了. 但是这部分代码之前在总的运行时长中占比仅为 1%(即$P=0.01$). 那么最终这个程序的加速比将接近$S_{latency}(s) = \frac{1}{(1 - 0.01) + \frac{0.01}{10}} = \frac{1}{0.99 + 0.001} \approx 1.009$. 性能提升了 0.9%. 很明显这样做的价值不大. 原因就是他找错了优化方向, 抓住了个芝麻粒大小的点在疯狂输出.