你的方案每次update都要从堆上分配,内存分配开销极大,而且地址不固定,缓存命中率很差,而且还有给堆增加大量碎片的副作用。而且每次还需要对引用计数归零的内存进行回收,又是额外的开销。
我的方案内存地址是固定的,可以一直被缓存,而且update没有内存分配,除非新字符串参数超过原先的std::wstring的预分配长度,才会进行一次缓冲区扩大。
实际上,你的方案比多线程加锁还慢。你随便问个AI就可以得到答案,比如你那个豆包。
一亿次只写的极限压力,两种做法的核心开销可以简化为:
A. std::make_shared<std::wstring>
≈ 1 次堆分配(对象+控制块一次性分配) + 1 次堆释放(旧块被 shared_ptr 原子减到 0 时) + 2 次原子修改(新块引用计数从 0→1,旧块从 1→0 并触发析构)
B. 对同一块字符串加锁后原地写入
≈ 1 次加锁/解锁(通常两条原子指令:lock xadd / mov) + 0 次内存分配
在现代 x86-64 Linux 上,实测数据(GCC13,-O3,Intel i7-12700):
malloc/free 一次 16-24 B 的块(std::wstring 空壳 + 控制块)≈ 35 ns
shared_ptr 原子增减一次 ≈ 5 ns
一次 uncontended std::mutex lock/unlock ≈ 15 ns
于是 1 亿次:
A 路:
(35 + 5×2) ns × 1e8 ≈ 4.5 s
B 路:
15 ns × 1e8 ≈ 1.5 s
结果:加锁原地写快 3 倍左右。
如果把字符串实际内容也考虑进去(>15 字节,SSO 失效),A 路还会再增加一次内部内存分配,差距继续拉大;而 B 路只多一次 memcpy,锁耗时不变。
因此,只写 1 亿次的极限场景,加锁原地更新显著快于不断 make_shared 并抛弃旧串。
【 在 smartbear 的大作中提到: 】
: 就事论事,我再老年人,也可以秒你,也不会写这么垃圾的代码,让豆包写一下,看看豆包听你的还是我的?都这年纪了,还没有学会就事论事,天天都扯西扯。
--
FROM 171.221.52.*