我在做一个跨语言的算子引擎,同一套语义分别用 Go、Java、C++ 实现。有一处用到了读写锁——一个请求局部的数据结构,多个算子并发读它的字段,偶尔有写。三个实现都用各自语言「标准」的读写锁:Go 的 sync.RWMutex、Java 的 ReentrantReadWriteLock、C++ 的 std::shared_mutex。
某次做性能剖析时,我注意到一件有点意思的事:C++ 这边,读写锁的单次拿放在 profile 里是个能看见的开销;而 Go 和 Java 那边,同样形态的拿放几乎不出现在火焰图里。同样一把读写锁,凭什么 C++ 要贵这么多?
这篇文章记录我顺着这个疑问挖下去的整个过程:先用反汇编搞清楚 glibc 的 pthread_rwlock 到底慢在哪、Go 为什么便宜,然后把 Go 的算法移植到 C++(中间失败了一次),最后发现这个原理上完全正确的优化,在真实负载上根本测不出收益。这最后一点,可能是整件事里最值得说的。