败犬日报 2025-08-14
1. constexpr 函数不一定编译期求值
constexpr 标记在函数上的语义是,这个函数既可以编译期求值也可以运行期求值。甚至还可以用 if consteval 主动判断是否编译期求值返回不同结果。
constexpr 标记在变量上就可以强迫编译期求值了,所以可以 constexpr auto f = foo();
来让函数在编译期调用;或者直接把函数标记成 consteval。
2. 编译期计算和运行时不一定会有相同结果
编译期求值是 host 处进行的,而运行时函数调用是 target 处执行的。虽然 C++ 的常量求值器会模拟 target 的环境,但是浮点运算没法简单的模拟。这主要因为不同芯片舍入、非规格数的处理会有差别。
(似乎日报记过,没找到)
3. 数学函数(例如 std::sqrt)不是 constexpr
因为数学函数会被舍入模式影响,还可能设置 errno,也就是有全局状态的读写。C++26 开始变成 constexpr,做法就是上文的 if consteval。
参考文章:https://www.zhihu.com/question/265774676/answer/3471569930
4. 提高现代处理器架构的效率(文章)
https://semiengineering.com/can-todays-processor-architectures-be-made-more-efficient/
5. 理解与实现 SIMD 函数(文章)
https://johnnysswlab.com/the-messy-reality-of-simd-vector-functions/
6. 面向延迟敏感型应用的内存管理机制(文章)
7. Analyzing Computer System Performance with Perl PDQ(书)
群友的分享:
看完第一章了,这书和 perl 没啥关系,第一张分析的事怎么评估系统负载,吞吐量和延迟的关系。
第二章介绍了几个很老的性能采样工具,这段可以跳过,后面介绍了怎么评估负载率的采样结果是否满足泊松分布。
第三章讲了时间的一些概念,怎么理解时间,系统的时间戳,怎么精确计时,对响应时间的离散点怎么拟合成各种分布,一些特殊场景的响应时间统计,比如分布式服务器,可用性与停机时长的关系,故障率与可靠性,可靠性模型。
第四章讲了一堆排队论的基本概念,数学定义,little's law,并且通过几个版本的定律形式,推导出了单服务器系统,怎么用利用率以及平均服务时长计算任务的平均驻留时长,怎么用利用率计算平均队列长度,这样得到了怎么理解利用率这个指标的几个经验法则。然后把这些公式扩展到了多服务器系统,在多服务器系统上,高负载情况通过数学推理能够得到一些反直觉的结果。在这之后,给出了Erlang的C公式和B公式,用于更精确的分析多服务器系统高负载情况下的繁忙概率和丢弃任务的概率。用这些公式,可以估算系统要达到某个可用指标,应该怎么准确的扩容。后面补充介绍了几种服务器,实际上是轮询算法和任务流程。接下来推导了 PK 方程,指导怎么根据历史指标,计算每个队列的平均驻留时间,来辅助新的任务选择等待的队列,然后说了这个辅助决策的算法不如轮询。最后作者通用可拓展性定律之父介绍了通用可拓展性定律。
第五章开始考虑了多个计算机子系统的组合,比如硬盘 + 内存 + cpu,来分析多队列的情况,第四章的考虑的是多个相同性能的接口。
8. 怎么让变量在不同编译期条件下绑定不同类型的变量
类似 auto val = cond ? int{} : std::string{};
可以这么做:
auto val = [] {
if constexpr (cond) {
return int{};
} else {
return std::string{};
}
}();
注意如果 else 去掉会报错,代码如下,这是因为函数返回值类型推导要求,在 constexpr if 里的语句才可能不参与推导。
auto val = [] {
if constexpr (cond) {
return int{};
}
return std::string{}; // return type 'std::string' (aka 'basic_string<char>') must match previous return type 'int' when lambda expression has unspecified explicit return type
}();
9. constexpr std::source_location::current 丢失模板信息
#include <iostream>
#include <string_view>
#include <source_location>
template <class T>
constexpr std::string_view foo1() {
constexpr auto tmp = std::source_location::current();
return tmp.function_name();
}
template <class T>
constexpr std::string_view foo2() {
auto tmp = std::source_location::current();
return tmp.function_name();
}
int main() {
std::cout << foo1<int>() << '\n'; // constexpr std::string_view foo1()
std::cout << foo2<int>() << '\n'; // constexpr std::string_view foo2() [with T = int; std::string_view = std::basic_string_view<char>]
}
foo2 的 std::source_location
会尽可能反映调用点的信息,所以会把求值阶段推迟到实例化模板阶段以让 function_name
包含模板信息。
foo1 因为 std::source_location
不依赖模板参数,constexpr 就把求值时机提前了,自然在编译期丢失了模板信息。