GoMem - 高性能内存分配器库
GoMem 是一个为 Go 语言设计的高性能内存分配器库,从 Monibuca 项目中提取而来。
特性
- 多种分配策略: 支持单树和双树(AVL)分配算法
- 伙伴分配器: 可选的伙伴系统,用于高效的内存池管理
- 可回收内存: 支持内存回收,具有自动清理功能
- 可扩展分配器: 动态增长的内存分配器
- 内存读取器: 高效的多缓冲区读取器,支持零拷贝操作
构建标签
该库支持多个构建标签来自定义行为:
twotree
: 使用双树(AVL)实现替代单树 treapenable_buddy
: 启用伙伴分配器进行内存池管理disable_rm
: 禁用可回收内存功能以减少开销
安装
go get github.com/langhuihui/gomem
使用方法
基本内存分配
package main
import "github.com/langhuihui/gomem"
func main() {
// 创建一个可扩展的内存分配器
allocator := gomem.NewScalableMemoryAllocator(1024)
// 分配内存
buf := allocator.Malloc(256)
// 使用缓冲区...
copy(buf, []byte("Hello, World!"))
// 释放内存
allocator.Free(buf)
}
分段内存释放
package main
import "github.com/langhuihui/gomem"
func main() {
// 创建一个可扩展的内存分配器
allocator := gomem.NewScalableMemoryAllocator(1024)
// 分配一大块内存
buf := allocator.Malloc(1024)
// 使用内存的不同部分
part1 := buf[0:256] // 前256字节
part2 := buf[256:512] // 中间256字节
part3 := buf[512:1024] // 后512字节
// 填充数据
copy(part1, []byte("Part 1 data"))
copy(part2, []byte("Part 2 data"))
copy(part3, []byte("Part 3 data"))
// 分段释放内存 - 可以释放部分内存
allocator.Free(part1) // 释放前256字节
allocator.Free(part2) // 释放中间256字节
// 继续使用剩余内存
copy(part3, []byte("Updated part 3"))
// 最后释放剩余内存
allocator.Free(part3)
}
可回收内存
// 为批量操作创建可回收内存
allocator := gomem.NewScalableMemoryAllocator(1024)
rm := gomem.NewRecyclableMemory(allocator)
// 分配多个缓冲区
buf1 := rm.NextN(128)
buf2 := rm.NextN(256)
// 使用缓冲区...
copy(buf1, []byte("Buffer 1"))
copy(buf2, []byte("Buffer 2"))
// 一次性回收所有内存
rm.Recycle()
内存缓冲区操作
// 创建一个内存缓冲区
mem := gomem.NewMemory([]byte{1, 2, 3, 4, 5})
// 添加更多数据
mem.PushOne([]byte{6, 7, 8})
// 获取总大小和缓冲区数量
fmt.Printf("Size: %d, Buffers: %d\n", mem.Size, mem.Count())
// 转换为字节数组
data := mem.ToBytes()
内存读取器
// 创建一个内存读取器
reader := gomem.NewReadableBuffersFromBytes([]byte{1, 2, 3}, []byte{4, 5, 6})
// 读取数据
buf := make([]byte, 6)
n, err := reader.Read(buf)
// buf 现在包含 [1, 2, 3, 4, 5, 6]
并发安全
⚠️ 重要: Malloc 和 Free 操作必须在同一个协程中调用,以避免竞态问题。为了更优雅的使用,建议使用 gotask,可以在 Start
方法中申请内存,在 Dispose
方法中释放内存。
// ❌ 错误:不同的协程
go func() {
buf := allocator.Malloc(256)
// ... 使用缓冲区
}()
go func() {
allocator.Free(buf) // 竞态条件!
}()
// ✅ 正确:同一个协程
buf := allocator.Malloc(256)
// ... 使用缓冲区
allocator.Free(buf)
// ✅ 优雅:使用 gotask
type MyTask struct {
allocator *gomem.ScalableMemoryAllocator
buffer []byte
}
func (t *MyTask) Start() {
t.allocator = gomem.NewScalableMemoryAllocator(1024)
t.buffer = t.allocator.Malloc(256)
}
func (t *MyTask) Dispose() {
t.allocator.Free(t.buffer)
}
性能考虑
- 在高吞吐量场景中使用
enable_buddy
构建标签以获得更好的内存池性能 - 启用 RecyclableMemory 比禁用版本快53%,且内存使用更少
- 仅在不需要内存管理功能时使用
disable_rm
构建标签(减少复杂度但牺牲性能) - 单树分配器比双树分配器显著更快(分配操作快77-86%)
- 仅在需要更快查找操作时使用
twotree
构建标签(比单树快100%)
基准测试结果
以下基准测试结果在 Apple M2 Pro (ARM64) 和 Go 1.23.0 环境下获得:
单树 vs 双树分配器性能比较
操作类型 | 单树 (ns/op) | 双树 (ns/op) | 性能差异 | 胜出者 |
---|---|---|---|---|
基础分配 | 12.33 | 22.71 | 快84% | 单树 |
小内存分配 (64B) | 12.32 | 22.60 | 快84% | 单树 |
大内存分配 (8KB) | 12.14 | 22.61 | 快86% | 单树 |
顺序分配 | 1961 | 3467 | 快77% | 单树 |
随机分配 | 12.47 | 23.02 | 快85% | 单树 |
查找操作 | 3.03 | 1.51 | 快100% | 双树 |
获取空闲大小 | 3.94 | 4.27 | 快8% | 单树 |
关键发现:
- 单树分配器在内存分配操作上快77-86%
- 双树分配器仅在查找操作上快100%
- 由于分配性能更优,推荐在大多数用例中使用单树分配器
RecyclableMemory 性能比较(启用 vs 禁用)
操作类型 | 启用 RM (ns/op) | 禁用 RM (ns/op) | 性能差异 | 内存使用 |
---|---|---|---|---|
基础操作 | 335.2 | 511.9 | 快53% | 启用: 1536B/2 allocs, 禁用: 1788B/2 allocs |
多个分配 | - | 1035.1 | - | 禁用: 3875B/10 allocs |
Clone操作 | - | 53.7 | - | 禁用: 240B/1 alloc |
关键发现:
- 启用 RecyclableMemory 在基础操作上快53%
- 启用 RM 内存使用更少(1536B vs 1788B 基础操作)
- 启用 RM 提供真正的内存管理和回收功能
- 禁用 RM 使用简单的
make([]byte, size)
无内存池
内存分配器性能(单树)
基准测试 | 操作次数/秒 | 每次操作时间 | 内存/操作 | 分配次数/操作 |
---|---|---|---|---|
Allocate | 96,758,520 | 15.08 ns | 0 B | 0 |
AllocateSmall | 98,864,434 | 12.49 ns | 0 B | 0 |
AllocateLarge | 100,000,000 | 12.65 ns | 0 B | 0 |
SequentialAlloc | 1,321,965 | 942.2 ns | 0 B | 0 |
RandomAlloc | 96,241,566 | 12.79 ns | 0 B | 0 |
GetFreeSize | 303,367,089 | 3.934 ns | 0 B | 0 |
内存操作性能
基准测试 | 操作次数/秒 | 每次操作时间 | 内存/操作 | 分配次数/操作 |
---|---|---|---|---|
PushOne | 31,982,593 | 35.05 ns | 143 B | 0 |
Push | 17,666,751 | 70.40 ns | 259 B | 0 |
ToBytes | 119,496 | 11,806 ns | 106,496 B | 1 |
CopyTo | 417,379 | 2,905 ns | 0 B | 0 |
Append | 979,598 | 1,859 ns | 7,319 B | 0 |
Count | 1,000,000,000 | 0.3209 ns | 0 B | 0 |
Range | 32,809,593 | 36.08 ns | 0 B | 0 |
内存读取器性能
基准测试 | 操作次数/秒 | 每次操作时间 | 内存/操作 | 分配次数/操作 |
---|---|---|---|---|
Read | 10,355,643 | 112.4 ns | 112 B | 2 |
ReadByte | 536,228 | 2,235 ns | 56 B | 2 |
ReadBytes | 2,556,602 | 608.7 ns | 1,080 B | 18 |
ReadBE | 408,663 | 3,587 ns | 56 B | 2 |
Skip | 8,762,934 | 125.8 ns | 56 B | 2 |
Range | 15,608,808 | 70.99 ns | 80 B | 2 |
RangeN | 20,101,638 | 79.09 ns | 80 B | 2 |
LEB128Unmarshal | 356,560 | 3,052 ns | 56 B | 2 |
伙伴分配器性能
基准测试 | 操作次数/秒 | 每次操作时间 | 内存/操作 | 分配次数/操作 |
---|---|---|---|---|
Alloc | 4,017,826 | 388.2 ns | 0 B | 0 |
AllocSmall | 3,092,535 | 410.7 ns | 0 B | 0 |
AllocLarge | 3,723,950 | 276.4 ns | 0 B | 0 |
SequentialAlloc | 62,786 | 17,997 ns | 0 B | 0 |
RandomAlloc | 3,249,220 | 357.8 ns | 0 B | 0 |
Pool | 27,800 | 56,846 ns | 196,139 B | 0 |
NonPowerOf2 | 3,167,425 | 317.8 ns | 0 B | 0 |
性能总结
- 单树分配器: 极快的分配/释放操作,每次操作约12ns,零内存分配
- 双树分配器: 分配较慢(约23ns每次操作),但查找操作更快(约1.5ns vs 3ns)
- 启用 RecyclableMemory: 比禁用版本快53%,内存效率更高
- 禁用 RecyclableMemory: 实现更简单但性能较慢,内存使用更高
- 内存操作: 高效的缓冲区管理,开销最小
- 内存读取器: 高性能读取,支持零拷贝操作
- 伙伴分配器: 快速的2的幂次分配,支持池化以减少GC压力
推荐:
- 由于分配性能更优,推荐在大多数应用中使用单树分配器(默认)
- 保持 RecyclableMemory 启用(默认)以获得更好的性能和内存效率
- 仅在查找操作关键且频繁时才使用双树分配器
- 仅在不需要内存管理功能时才使用
disable_rm
标签

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
黑客声称窃取红帽 570GB 内部数据
开源巨头 Red Hat 近日证实,其内部部署的一套 GitLab 实例遭遇黑客入侵,引发外界广泛关注。该事件最初被误传为 “GitHub 被攻破”,但 Red Hat 随后澄清:被入侵的并非 GitHub,而是其用于咨询业务的私有 GitLab 系统。 根据自称为 Crimson Collective 的黑客组织声称,他们从 Red Hat 系统中窃取了约 570GB 的数据,涉及 2.8 万个内部项目和约 800 份客户参与报告(CER)。这些文件疑似包含企业网络架构、访问令牌、认证凭证等敏感内容,黑客甚至公开部分文件清单作为“证据”。 Red Hat 表示,事件已上报相关部门并启动全面调查,入侵系统已被隔离,目前没有迹象显示公司其他核心产品、服务或供应链受到影响。公司也正在通知可能受波及的客户并加强安全防护措施。
-
下一篇
苹果公司对其漏洞悬赏计划提供更丰厚的奖励
苹果公司宣布将其安全漏洞悬赏奖金上限提升至200万美元,以吸引更多网络安全专家参与其Bug悬赏计划。这项奖金调整将于2025年11月正式生效,同时针对特定难度较高的攻击类型,通过奖励机制,单项奖金最高可达500万美元。 长期以来,苹果漏洞悬赏计划曾因奖金较低、支付流程冗长而遭业内批评。尽管并非业界最低,但苹果因支付不积极而备受质疑。此次调整,旨在吸引更多顶尖安全研究人员参与苹果产品的安全攻防。 苹果安全工程与架构副总裁Ivan Krstic表示:“针对最难攻破的类别、最复杂的问题,以及那些最像雇佣间谍软件攻击的短板,我们希望让掌握相关技能、付出努力的安全专家获得丰厚奖励。” 新的悬赏方案扩展至更多安全类别,包括无线距离攻击和一键式WebKit沙箱突破等,同时引入“Target Flag”系统,允许提交者在漏洞未修复前先获得奖金。 为鼓励新人,苹果将以往试行的低影响漏洞1000美元奖励设为永久项目。自2016年专向专家注册计划启动以来,苹果安全悬赏还保持2025年度注册开放至10月31日。 值得关注的是,本次调整不仅针对注册专家,扩展至公开的Apple Security Bounty计划...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 面试大杂烩
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- MySQL8.0.19开启GTID主从同步CentOS8