败犬日报 2025-02-20
1. 群友出的面试题
https://www.zhihu.com/question/451327108/answer/53592485454
对此群友讨论了下面的话题。
1.1. magic static
void swapTwoPoints(std::pair<double,double>& a, std::pair<double,double>& b) {
static std::pair<double,double> tmp = a;
a = b;
b = tmp;
return;
}
这个代码的 bug 是静态局部变量只有首次调用会初始化,第二次调用就寄了。
magic static 就是用运行时的值去初始化静态局部变量,效果是只会初始化一次,还能保证并发安全。
效果和 std::call_once
相同。
magic static 可以用来实现单例模式。
1.2. double 传值还是传常量引用
传值。
一般来说,double 只有 8 字节,传引用(在实现上是传递 double 变量的地址)也是 8 字节。但是传引用需要解引用,这是一笔开销。
这里不考虑内联,内联了就没区别。
24 字节以内结构体都推荐传值。
2. 拷贝构造的参数不加引用
struct T {
T(const T rhs) { ... }
};
不行。因为 rhs 传参的时候,它需要被初始化。而“初始化”这个过程依赖构造函数,这样就会无限递归。
事实上编译器会报错 error: invalid constructor; you probably meant 'T (const T&)'
。
3. rust 引用计数溢出导致安全问题
https://internals.rust-lang.org/t/rc-is-unsafe-mostly-on-32-bit-targets-due-to-overflow/2120
有点离谱。
4. 为什么 L1 Cache 只有页内的位才能索引
缓存一致性问题。
虚拟地址分为页号(高位部分)和页内偏移(低位部分)。如果两个虚拟地址映射到同一物理地址时,这个物理地址就可以映射到不同的缓存组中。而虚拟 / 物理地址的页内偏移是一致的(翻译前后不变),就不会出现这个问题。
5. 加 prefetch 性能是否有提升
经常没有提升。
因为一般 CPU 的硬件预取能覆盖常见的访问模式,不用再做软件预取了,简单的 CPU 除外。另外一些算法的特殊访问模式也会对预取效果有影响。
所以满足上述条件的可以多花时间研究 prefetch。
6. float 转换为相同二进制表示的 int
标准做法是 std::bit_cast
(C++20),旧版本用 memcpy
。不用担心性能,memcpy 第三个参数是常量会被编译器优化掉,从而没有函数调用开销。
int convert(float x) {
int y;
memcpy(&y, &x, sizeof(int));
return y;
}
不标准但可行的做法是搞个 union
。https://zh.cppreference.com/w/cpp/language/union 有记载:
读取并非最近写入的联合体成员是未定义行为。许多编译器以非标准语言扩展实现读取联合体的不活跃成员的能力。
不要用 reinterpret_cast
,这个是未定义行为,有类型别名问题。