bvar | bRPC
本文最后更新于:2020年5月26日
bvar 是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值。
特性
- bvar 不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读时需要合并所有写过的线程中的数据, 而不可避免地变慢了,所以读写都很频繁或需要基于最新值做一些逻辑判断时,不应该使用 bvar。
- 当很多线程都在累加一个计数器时,每个线程只累加私有的变量而不参与全局竞争,在读取时累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频的记录和展现,慢点无所谓。
相关知识
原子操作
原子操作有时会很慢。一个核心写入自己的 L1 cache 是极快的(4 cycles,约 2ns,但当另一个核心读或写同一处内存时,它得确认看到其他核心中对应的 cacheline。对于软件来说,这个过程是原子的,不能在中间穿插其他代码,只能等待 CPU 完成一致性同步,这个复杂的硬件算法使得原子操作会变得很慢。
cache bouncing
当很多线程在频繁修改某个字段时,这个字段所在的 cache line 被不停地同步到不同的核上,就像在核间弹来弹去,这个现象就叫做 cache bouncing。
memory fence
memory fence 简单来说就是串行化加载与存储操作,主要是为了防止 CPU 乱序执行导致的错误。以下面的代码为例。
// Thread 1
// ready was initialized to false
p.init();
ready = true;
// Thread2
if (ready) {
p.bar();
}
有一定的概率 Thread 1 中的指令被乱序执行,这时 Thread 2 中的指令就会在错误的情况下执行。boost 和 C++11 对 memory fence 做了抽象,上面的例子可以这么更正:
// Thread1
// ready was initialized to false
p.init();
ready.store(true, std::memory_order_release); // 前面访存指令勿重排至此条指令之后。当此条指令的结果对其他线程可见后,之前的所有指令都可见
// Thread2
if (ready.load(std::memory_order_acquire)) { // 后面访存指令勿重排至此条指令之前
p.bar();
}
参考
评论系统采用 utterances ,加载有延迟,请稍等片刻。