惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
Hugging Face - Blog
Hugging Face - Blog
博客园 - 聂微东
量子位
S
SegmentFault 最新的问题
V
Visual Studio Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
小众软件
小众软件
Stack Overflow Blog
Stack Overflow Blog
Vercel News
Vercel News
D
Docker
J
Java Code Geeks
博客园 - 三生石上(FineUI控件)
博客园 - Franky
Recent Announcements
Recent Announcements
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
D
DataBreaches.Net
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
V
V2EX

Random Thoughts

尽职编程:AI Coding 时代的个体产出差异的来源 大公司与创业公司的 AI Coding 体验差异 LLM 训练与推理的基本理解 昨日的世界不再重来 Magic Brush - 画出你自己的产品宇宙 写在 AI Coding 奇点之后 尼泊尔布恩山小环线纪行 2025 投资组合年报 三十而笠 我的 8 年职业生涯回顾 我的颈椎病康复之旅 - 关于选择的故事 熊野古道中边路纪行 新加坡旅居三年再回首 高加索三国 - 阿塞拜疆行记 2024 投资组合年报 重新思考 Go:了解程序在线上是如何运行的 欧游散记 —— 环勃朗峰之旅 欧游散记 —— 特摩索斯古城 重新思考 Go:Channel 不是「消息队列」 重新思考 Go:Slice 只是「操作视图」 那一天,我决定踏出一步 True Story 少数价值 A Snapshot of Myself - 2021 RPC 漫谈: 连接问题 RPC 漫谈:序列化问题 RPC 漫谈: 限流问题 科学,技术与工程 Pond: Golang 通用对象池 Golang for-range 内部实现
Golang rand 库锁竞争优化
Joway · 2020-12-17 · via Random Thoughts

背景

最近在实现一个随机负载均衡器的时候发现一个问题,在高并发的情况下,官方标准库 rand.Intn() 性能会急剧下降。翻了下实现以后才发现它内部居然是全局共享了同一个 globalRand 对象。

一段测试代码:

func BenchmarkGlobalRand(b *testing.B) {
   b.RunParallel(func(pb *testing.PB) {
      for pb.Next() {
         rand.Intn(100)
      }
   })
}

func BenchmarkCustomRand(b *testing.B) {
   b.RunParallel(func(pb *testing.PB) {
      rd := rand.New(rand.NewSource(time.Now().Unix()))
      for pb.Next() {
         rd.Intn(100)
      }
   })
}

输出:

BenchmarkGlobalRand
BenchmarkGlobalRand-8 18075486 66.1 ns/op
BenchmarkCustomRand
BenchmarkCustomRand-8 423686118 2.38 ns/op

解决思路

最理想对情况是可以在每个 goroutine 内创建一个私有的 rand.Rand 对象,从而实现真正的无锁。

但在很多其他场景下,我们并不能直接控制调用我们的 goroutine,又或者 goroutine 数量过多以至于无法承受这部分内存成本。

此时的一个思路是使用 sync.Pool 来为 rand.Source 创建一个池,当多线程并发读写时,优先从自己当前 P 中的 poolLocal 中获取,从而减少锁的竞争。同时由于只是用 pool 扩展了原生的 rngSource 对象,所以可以兼容其 rand.Rand 下的所有接口调用。

基于这个思路,实现了一个 fastrand 库放到了 github。

从 benchmark 中可以看到性能提升显著,在并发条件下,比原生全局 rand 快了大约 8 倍.

BenchmarkStandardRand
BenchmarkStandardRand-8                         60870416                19.1 ns/op             0 B/op          0 allocs/op
BenchmarkFastRand
BenchmarkFastRand-8                             100000000               10.7 ns/op             0 B/op          0 allocs/op
BenchmarkStandardRandWithConcurrent
BenchmarkStandardRandWithConcurrent-8           18058663                67.8 ns/op             0 B/op          0 allocs/op
BenchmarkFastRandWithConcurrent
BenchmarkFastRandWithConcurrent-8               132542940                8.79 ns/op            0 B/op          0 allocs/op