Skip to content

败犬日报 2024-10-16

1. std::mutex 应该用 RAII 不推荐手动 unlock()

因为如果上锁的部分中有比较复杂的控制流(例如异常),那还需要记得在控制流的每一个出口手动 unlock(),否则就会出问题。

C++ 中,几乎所有情况都不应该使用原始 mutex 的 lock / unlock,而用 lock_guard / unique_lock 等更高层次的封装代替。

2. 线程安全哈希表 benchmark

img

完整 benchmark 见 https://www.boost.org/doc/libs/develop/libs/unordered/doc/html/unordered.html#benchmarks_boostconcurrent_flat_map

boost 的项目足够新,后发优势,可以抄其他项目的优秀设计,flat_hashmap 性能可能是知名实现里最好的。

3. lambda 的捕获是发生在什么时候

cpp
auto GetCurrentTimeNanos = []() -> int64_t {
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return ts.tv_sec * 1000000000ll + ts.tv_nsec;
};
auto func = [&, start_time = GetCurrentTimeNanos()]() {
    int64_t end_time = GetCurrentTimeNanos();
};

cppref 没说明白,标准说明白了的,在 lambda 求值时(创建时)初始化捕获:https://eel.is/c++draft/expr.prim.lambda#capture-15

4. clang-tidy 报变量没有被使用过

[[maybe_unused]]

一个通用的查找方式:clang-tidy 报的这个 warning 是有一个代号的,直接搜这个,会看到 clang-tidy 的文档。

5. 有什么办法可以萃取获得 lambda 函数的返回值和参数列表吗

lambda::operator() 萃取。

6. 浮点运算的常数折叠行为有差异吗

有。

https://github.com/llvm/llvm-project/issues/62479

7. std::vector 扩容系数 1.5 为什么比 2 好

我们知道,std::vector 在容量不够时会申请新的一块空间,这个空间大小 = 原来的大小乘以扩容系数。MSVC 是 1.5,GCC / Clang 是 2。

唯一正解:实测 1.5 比 2 性能好!


folly 文档是这么写的:1.5 不一定能实现内存复用,但存在复用的概率;而 2 不存在理论复用的可能。这里的内存复用是怎么一回事呢?

我们考虑一个简单的场景:不断给一个 vector push_back 操作。

假设一开始 vector 容量是 V,扩容系数 p,那么接下来的 n 次扩容容量分别是:V,Vp,Vp2,...,Vpn

接下来一次扩容,会申请 Vpn+1 的容量(并且这次申请我们打算复用之前用过的内存)。此时之前已经释放了 V,Vp,Vp2,...,Vpn1 的内存(注意 Vpn 还没释放),这些内存加起来大于等于即 Vpn+1 就可以内存复用。

V+Vp+Vp2+...+Vpn1=V(pn1)p1Vpn+1

考虑 n 趋向于无穷大:

Vpnp1Vpn+11p1pp(p1)15+12p5+12

p1.618 就是这么来的。


但是实际上内存分配器行为并不是那样的,内存复用在大多数情况都没有说服力(内存分配器一般会把大小近似的内存块放一起,导致复用的内存几乎不可能是同一个 vector 扩容留下的)。

我们知道,扩容系数越小内存占用期望降低,内存利用率越高;扩容系数越大扩容次数变少,扩容开销降低。之所以后来的很多设计采用了 1.5 而不是 2,因为 1.5 是两种因素的平衡点,是从大量实践中找到的。https://groups.google.com/g/comp.lang.c++.moderated/c/asH_VojWKJw?pli=1

关于 GCC 扩容系数为什么用 2:可能 GCC 实现太早了,没有意识到 1.5 更好。

相关阅读

8. realloc 是一个糟糕的设计

没时间读了先放这