Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve comment #22

Merged
merged 2 commits into from
Feb 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@

# Dependency directories (remove the comment below to include it)
# vendor/

.idea
61 changes: 31 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,41 @@

<img src=https://raw.githubusercontent.com/heiyeluren/XMM/main/docs/img/xmm-logo02.png width=50% />
<p>
<a href="https://sourcegraph.com/github.com/heiyeluren/XMM?badge"><img src="https://sourcegraph.com/github.com/heiyeluren/XMM/-/badge.svg" alt="GoDoc"></a>
<a href="https://pkg.go.dev/github.com/heiyeluren/XMM"><img src="https://pkg.go.dev/badge/github.com/heiyeluren/XMM" alt="GoDoc"></a>
<a href="https://goreportcard.com/report/github.com/heiyeluren/XMM"><img src="https://goreportcard.com/badge/github.com/heiyeluren/XMM" alt="Go Report Card" /a>
</p>


## ➤ [XMM English Introduction Document](https://github.com/heiyeluren/XMM/blob/main/docs/XMM-INTRO-EN.md)


<br />
<br />


## ➤ XMM中文文档

## XMM (eXtensible) Memory Manager - 完全自主第三方 Go 内存分配管理器

- [XMM (eXtensible) Memory Manager - 完全自主第三方 Go 内存分配管理器](#xmm-extensible-memory-manager---完全自主第三方-go-内存分配管理器)
- [XMM 是什么?](#xmm-是什么)
- [XMM 主要具备以下特点](#xmm-主要具备以下特点)
- [为什么要设计 XMM?](#为什么要设计-xmm)
- [为什么要设计自主的内存管理器?](#为什么要设计自主的内存管理器)
- [为什么不使用内置的 map/slice 等数据结构?](#为什么不使用内置的-mapslice-等数据结构)
- [为什么不使用其他开源的内存池?](#为什么不使用其他开源的内存池)
- [XMM 的最终设计结论是什么?](#xmm-的最终设计结论是什么)
- [XMM 设计的目标是什么?](#xmm-设计的目标是什么)
- [XMM 快速使用入门](#xmm-快速使用入门)
- [☆ XMM 使用案例 ☆](#-xmm-使用案例-)
- [XMM 实现原理介绍](#xmm-实现原理介绍)
- [XMM 技术交流](#xmm-技术交流)



- [XMM 是什么?](#xmm-是什么)
- [XMM 主要具备以下特点](#xmm-主要具备以下特点)
- [为什么要设计 XMM?](#为什么要设计-xmm)
- [为什么要设计自主的内存管理器?](#为什么要设计自主的内存管理器)
- [为什么不使用内置的 map/slice 等数据结构?](#为什么不使用内置的-mapslice-等数据结构)
- [为什么不使用其他开源的内存池?](#为什么不使用其他开源的内存池)
- [XMM 的最终设计结论是什么?](#xmm-的最终设计结论是什么)
- [XMM 设计的目标是什么?](#xmm-设计的目标是什么)
- [XMM 快速使用入门](#xmm-快速使用入门)
- [☆ XMM 使用案例 ☆](#-xmm-使用案例-)
- [XMM 实现原理介绍](#xmm-实现原理介绍)
- [XMM 技术交流](#xmm-技术交流)

<br />

## XMM 是什么?

XMM - X(eXtensible) Memory Manager(完全自主研发的第三方 Go 内存分配管理器)

XMM 是一个在 Go 语言环境中完全自主实现的第三方内存管理库,不依赖于 Go 本身的任何内存管理能力,纯自主实现的 Go 内存管理库;能够应对各种场景下大小内存的 分配/释放/管理 等工作,能够帮助适用于任何复杂数据结构的构建(链表/数组/树/hash 等场景),能够良好完美的逃逸掉 Go 内置的 GC 机制,保证程序的超高性能,是构建高性能程序基础设施。
XMM 是一个在 Go 语言环境中完全自主实现的第三方内存管理库,不依赖于 Go 本身的任何内存管理能力,纯自主实现的 Go 内存管理库;能够应对各种场景下大小内存的 分配/释放/管理
等工作,能够帮助适用于任何复杂数据结构的构建(链表/数组/树/hash 等场景),能够良好完美的逃逸掉 Go 内置的 GC 机制,保证程序的超高性能,是构建高性能程序基础设施。

<br />

Expand All @@ -50,11 +45,13 @@ XMM 是一个在 Go 语言环境中完全自主实现的第三方内存管理库

2. XMM 能够应对各种场景下大小内存的 分配/释放/管理 等工作,能够帮助适用于任何复杂数据结构的构建,比如链表/数组/树/哈希表等等场景;XMM 可以让你像 C/C++ 一样方便便捷使用系统内存,并且不用担心性能问题。

3. XMM 能够良好完美的逃逸掉 Go 内置的 GC 机制,保证程序的超高性能,是构建高性能程序基础设施;但与 sync.Pool 等实现机制完全不同,sync.Pool 等使用字节流实现来逃逸 GC,XMM 是纯使用 Linux 系统的 mmap 作为底层内存存储,XMM 更像 TcMalloc 等内存分配器。
3. XMM 能够良好完美的逃逸掉 Go 内置的 GC 机制,保证程序的超高性能,是构建高性能程序基础设施;但与 sync.Pool 等实现机制完全不同,sync.Pool 等使用字节流实现来逃逸 GC,XMM 是纯使用 Linux 系统的
mmap 作为底层内存存储,XMM 更像 TcMalloc 等内存分配器。

4. XMM 协程安全,且分配性能超高,目前在普通 Linux 服务器上面可以达到 350w alloc/s,就是每秒可以进行 350 万次的内存分配操作不卡顿,非常适合想要自主管理内存且超高性能场景。

5. XMM 内存库使用接口简单,兼容性强,能够兼容 Go 1.8 以上版本,容易上手(推荐 go 1.12+ 版本更好),可以在 XMM 之上重构你所有想要的高性能数据结构,比如 map/slice 等等。(案例部分可以做一些数据结构实现的参考)
5. XMM 内存库使用接口简单,兼容性强,能够兼容 Go 1.8 以上版本,容易上手(推荐 go 1.12+ 版本更好),可以在 XMM 之上重构你所有想要的高性能数据结构,比如 map/slice
等等。(案例部分可以做一些数据结构实现的参考)

<br />
<br />
Expand All @@ -65,35 +62,41 @@ XMM 是一个在 Go 语言环境中完全自主实现的第三方内存管理库

### 为什么要设计自主的内存管理器?

为了应对在多种内存管理的场景下的使用,可能需要有一些除了内置数据结构外的一些内存自主调度使用的场景,比如构建复杂的高性能的数据结构,在大规模内存占用,或者是非常多的小内存块占用场景下,能够尽量减少 Go 的 GC 机制,保障服务性能稳定不会因为 GC 而产生抖动。
为了应对在多种内存管理的场景下的使用,可能需要有一些除了内置数据结构外的一些内存自主调度使用的场景,比如构建复杂的高性能的数据结构,在大规模内存占用,或者是非常多的小内存块占用场景下,能够尽量减少 Go 的 GC
机制,保障服务性能稳定不会因为 GC 而产生抖动。

<br />


### 为什么不使用内置的 map/slice 等数据结构?

Golang 本身为了性能和内存可控,整个内存管理是完全封闭不对外的,并且有自主的 GC 机制,需要自主内存管理比较麻烦;Go 中自带的 GC 机制经过很多个版本的迭代,到目前性能已经很不错,但是在大规模的碎片化内存块下面,GC 还是会有一定损耗,在极端高性能场景下,GC 会让整个后台应用服务性能上不去(或偶尔卡顿)。所以一句话,Go 本身指针等还有性能会受到 GC 的影响,导致服务性能总是上不去。
Golang 本身为了性能和内存可控,整个内存管理是完全封闭不对外的,并且有自主的 GC 机制,需要自主内存管理比较麻烦;Go 中自带的 GC 机制经过很多个版本的迭代,到目前性能已经很不错,但是在大规模的碎片化内存块下面,GC
还是会有一定损耗,在极端高性能场景下,GC 会让整个后台应用服务性能上不去(或偶尔卡顿)。所以一句话,Go 本身指针等还有性能会受到 GC 的影响,导致服务性能总是上不去。
<br />

### 为什么不使用其他开源的内存池?

1. 除 Go 本身的内存模块,调研了解现有大部分的第三方 对象池/内存池/字节池 等需要某块自主内存操作的场景中基本是 Map/sync.Pool/Bytes[] 等方式。

2. Map 数据结构适合保存各类型数据,但 GC 概率大; sync.Pool 这类保存复用临时对象,也可以各种数据机构,可适当减少 GC(无法避免 GC); Bytes[] 方式来保存字节数据,并且只能保存字节数据,通过某些处理,尽量逃避 GC 扫描;(对比参考 [Go 语言基于 channel 实现的并发安全的字节池](https://zhuanlan.zhihu.com/p/265790840) )
2. Map 数据结构适合保存各类型数据,但 GC 概率大; sync.Pool 这类保存复用临时对象,也可以各种数据机构,可适当减少 GC(无法避免 GC); Bytes[]
方式来保存字节数据,并且只能保存字节数据,通过某些处理,尽量逃避 GC 扫描;(对比参考 [Go 语言基于 channel 实现的并发安全的字节池](https://zhuanlan.zhihu.com/p/265790840) )

3. 现有开源库包括:依赖于 sync.Pool 的比如字节的 mcache [gopkg/mcache.go](https://github.com/bytedance/gopkg/blob/main/lang/mcache/mcache.go);采用 Bytes[] 方式的比如 MinIO 的的 [bpool minio/bpool.go](https://github.com/minio/minio/blob/master/internal/bpool/bpool.go) ,都可以学习参考。
3. 现有开源库包括:依赖于 sync.Pool 的比如字节的
mcache [gopkg/mcache.go](https://github.com/bytedance/gopkg/blob/main/lang/mcache/mcache.go);采用 Bytes[] 方式的比如 MinIO
的的 [bpool minio/bpool.go](https://github.com/minio/minio/blob/master/internal/bpool/bpool.go) ,都可以学习参考。

4. 结论:XMM 与他们实现机制完全不同,XMM 更靠近 Go 内置内存分配机制原理

<br />

### XMM 的最终设计结论是什么?

为了完全实现最终为了逃逸掉 Golang 的 GC 机制,以及拥有完全自主可控的内存管理分配操作,在面对成千上万的小对象场景中,不会因为 Go 本身 GC 机制带来任何的抖动,所以自主从零开始实现了 XMM 模块,达到在 Go 程序中调用 XMM 模块可以达到完美的自主内存 申请/释放/管理 的功能,并可以完美逃逸掉 Go 的 GC 机制。
为了完全实现最终为了逃逸掉 Golang 的 GC 机制,以及拥有完全自主可控的内存管理分配操作,在面对成千上万的小对象场景中,不会因为 Go 本身 GC 机制带来任何的抖动,所以自主从零开始实现了 XMM 模块,达到在 Go 程序中调用
XMM 模块可以达到完美的自主内存 申请/释放/管理 的功能,并可以完美逃逸掉 Go 的 GC 机制。
<br />
<br />

### XMM 设计的目标是什么?

为了保证高性能,XMM 设计之初,就定下了三个核心目标:

1. 单机(6 核心 KVM 或物理机)内存分配性能达到 350w+ alloc/s;(每秒内存分配速度);
Expand All @@ -102,7 +105,6 @@ Golang 本身为了性能和内存可控,整个内存管理是完全封闭不

3. 不会内存泄露,并且内存管理不是粗糙的,而颗粒度细致的,完全尽量可媲美行业主流的内存管理分配器。


<br />
<br />

Expand All @@ -121,7 +123,6 @@ Golang 本身为了性能和内存可控,整个内存管理是完全封闭不

<br /> <br />


## XMM 实现原理介绍

1. [XMM 的核心设计与实现流程](https://github.com/heiyeluren/XMM/blob/main/docs/XMM-DesignImplementation.md)
Expand Down
8 changes: 4 additions & 4 deletions bit_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,14 @@ func (m markBits) setMarked() {
// Might be racing with other updates, so use atomic update always.
// We used to be clever here and use a non-atomic update in certain
// cases, but it's not worth the risk.
//atomic.Or32(m.uint32p, m.mask)
// atomic.Or32(m.uint32p, m.mask)
for {
val := atomic.LoadUint32(m.uint32p)
if atomic.CompareAndSwapUint32(m.uint32p, val, val^m.mask) {
return
}
}
//atomic.StoreUint32(m.uint32p, *m.uint32p^m.mask)
// atomic.StoreUint32(m.uint32p, *m.uint32p^m.mask)
}

// setMarkedNonAtomic sets the marked bit in the markbits, non-atomically.
Expand All @@ -172,14 +172,14 @@ func (m markBits) clearMarked() {
// Might be racing with other updates, so use atomic update always.
// We used to be clever here and use a non-atomic update in certain
// cases, but it's not worth the risk.
//atomic.And32(m.uint32p, ^m.mask)
// atomic.And32(m.uint32p, ^m.mask)
for {
val := atomic.LoadUint32(m.uint32p)
if atomic.CompareAndSwapUint32(m.uint32p, val, val&(^m.mask)) {
return
}
}
//atomic.StoreUint32(m.uint32p, *m.uint32p&(^m.mask))
// atomic.StoreUint32(m.uint32p, *m.uint32p&(^m.mask))
}

// markBitsForSpan returns the markBits for the span base address base.
Expand Down
6 changes: 3 additions & 3 deletions bit_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ func TestMarkBits(t *testing.T) {
if err != nil {
t.Fatal(err)
}
//fmt.Println(String(bit, 4), len(String(bit, 4)))
// fmt.Println(String(bit, 4), len(String(bit, 4)))

bytep, mask := bit.bitp(objIndex)
mb := markBits{bytep, mask, objIndex}
fmt.Printf("bytep:%.32b , mask:%.32b isMarked:%t \n", *bytep, mask, mb.isMarked())
mb.setMarked()
//fmt.Println(String(bit, 4))
// fmt.Println(String(bit, 4))
bytep, mask = bit.bitp(objIndex)
mb = markBits{bytep, mask, objIndex}

fmt.Printf("bytep:%.32b , mask:%.32b isMarked:%t \n", *bytep, mask, mb.isMarked())

//fmt.Println(String(bit, 4))
// fmt.Println(String(bit, 4))
bytep, mask = bit.bitp(uintptr(5000000))
mb = markBits{bytep, mask, objIndex}

Expand Down
2 changes: 1 addition & 1 deletion chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

package xmm

//连续page的管理
// 连续page的管理
type xChunk struct {
startAddr uintptr
npages uintptr
Expand Down
24 changes: 12 additions & 12 deletions class_span.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ import (

type xClassSpan struct {

//lock sync.Mutex

// lock sync.Mutex
classIndex uint // class的索引

//空的,
// 空的,
free *mSpanList

//满的span。
// 满的span。
full *mSpanList

// nmalloc is the cumulative count of objects allocated from
Expand All @@ -42,6 +41,7 @@ type xClassSpan struct {
heap *xHeap
}

// Init initializes
func (x *xClassSpan) Init(classIndex uint, heap *xHeap) error {
if heap == nil {
return errors.New("heap is nil")
Expand All @@ -59,7 +59,7 @@ func (x *xClassSpan) allocSpan(index int, f float32) (*xSpan, error) {
return x.free.moveHead(), nil
}()
if span != nil && err == nil {
//log.Printf("xClassSpan class:%d free申请 span:%d\n", x.classIndex, unsafe.Pointer(span))
// log.Printf("xClassSpan class:%d free申请 span:%d\n", x.classIndex, unsafe.Pointer(span))
return span, nil
}
}
Expand All @@ -68,7 +68,7 @@ func (x *xClassSpan) allocSpan(index int, f float32) (*xSpan, error) {
size := class_to_size[index]
pageNum = uint8(Align(Align(uintptr(size), _PageSize)/uintptr(_PageSize), uintptr(pageNum)))
span, err := heap.allocSpan(uintptr(pageNum), uint(index), uintptr(size), f)
//log.Printf("xClassSpan heap.allocSpan class:%d free申请 span:%d\n", x.classIndex, unsafe.Pointer(span))
// log.Printf("xClassSpan heap.allocSpan class:%d free申请 span:%d\n", x.classIndex, unsafe.Pointer(span))
if err != nil {
return nil, err
}
Expand All @@ -92,7 +92,7 @@ func (x *xClassSpan) freeSpan(span *xSpan) (swap bool, size uint, err error) {
needFree := false
err = func() error {
gcCount := span.countGcMarkBits()
//没有达到gc阈值或者当前span正在被分配不做GC(异步gc该类span)
// 没有达到gc阈值或者当前span正在被分配不做GC(异步gc该类span)
if gcCount <= uintptr(float64(span.nelems)*SpanGCFactor) {
return nil
}
Expand All @@ -104,7 +104,7 @@ func (x *xClassSpan) freeSpan(span *xSpan) (swap bool, size uint, err error) {
allocCount := span.allocCount
gcCount = span.countGcMarkBits()
x.heap.addFreeCapacity(-int64(allocCount))
//没有达到gc阈值或者当前span正在被分配不做GC(异步gc该类span)
// 没有达到gc阈值或者当前span正在被分配不做GC(异步gc该类span)
if gcCount < uintptr(float64(span.nelems)*SpanGCFactor) {
return nil
}
Expand All @@ -115,14 +115,14 @@ func (x *xClassSpan) freeSpan(span *xSpan) (swap bool, size uint, err error) {
needFree = true
span.freeIndex = 0
span.allocCount = span.nelems - gcCount
//span.gcmarkBits.show64(span.nelems)
// span.gcmarkBits.show64(span.nelems)
span.allocBits = span.gcmarkBits
span.gcmarkBits, err = newMarkBits(span.nelems, true)
if err != nil {
return err
}
span.refillAllocCache(0)
//log.Printf("refillAllocCache allocCache:%.64b gcCount:%d allocCount:%d gcCount:%d oldallocCount:%d\n", span.allocCache, gcCount, span.allocCount, gcCount, allocCount)
// log.Printf("refillAllocCache allocCache:%.64b gcCount:%d allocCount:%d gcCount:%d oldallocCount:%d\n", span.allocCache, gcCount, span.allocCount, gcCount, allocCount)
return nil
}()
if err != nil {
Expand All @@ -131,8 +131,8 @@ func (x *xClassSpan) freeSpan(span *xSpan) (swap bool, size uint, err error) {
if !needFree {
return false, 0, nil
}
//判断当前span是否不在使用,不在使用存放进去。在使用则
//log.Printf("xClassSpan class:%d 回收 span:%d\n", x.classIndex, unsafe.Pointer(span))
// 判断当前span是否不在使用,不在使用存放进去。在使用则
// log.Printf("xClassSpan class:%d 回收 span:%d\n", x.classIndex, unsafe.Pointer(span))
x.free.insert(span)
return true, size, nil
}
13 changes: 7 additions & 6 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
package xmm

import (
"github.com/spf13/cast"
"log"
"runtime/debug"
"sync"
"sync/atomic"
"unsafe"

"github.com/spf13/cast"
)

func Recover() {
Expand All @@ -37,7 +38,7 @@ func Recover() {
}
}

// align returns the smallest y >= x such that y % a == 0.
// Align returns the smallest y >= x such that y % a == 0.
func Align(x, a uintptr) uintptr {
if a < 1 {
return x
Expand Down Expand Up @@ -114,10 +115,10 @@ var oneBitCount = [256]uint8{
4, 5, 5, 6, 5, 6, 6, 7,
5, 6, 6, 7, 6, 7, 7, 8}

//mSpanList 支持并发的操作
// mSpanList 支持并发的操作
type mSpanList struct {
first *xSpan // first span in list, or nil if none
//last *xSpan // last span in list, or nil if none
// last *xSpan // last span in list, or nil if none
lock sync.Mutex
}

Expand All @@ -135,7 +136,7 @@ func (list *mSpanList) insert(span *xSpan) {
if first == nil && atomic.CompareAndSwapPointer(addr, nil, unsafe.Pointer(span)) {
return
}
//先将新插入的赋值first,这时候会断裂为两个链。然后再赋值。
// 先将新插入的赋值first,这时候会断裂为两个链。然后再赋值。
if first != nil && atomic.CompareAndSwapPointer(addr, first, unsafe.Pointer(span)) {
span.next = (*xSpan)(first)
return
Expand Down Expand Up @@ -171,7 +172,7 @@ func (list *mSpanList) move(span *xSpan) {
}
pre = node
}
//并发cas
// 并发cas
var addr *unsafe.Pointer
if pre != nil {
addr = (*unsafe.Pointer)(unsafe.Pointer(&pre.next))
Expand Down
2 changes: 1 addition & 1 deletion common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestSpanList(t *testing.T) {
if i != waitCnt {
t.Fatal(u, "的数目不对 cnt:", i)
}
//fmt.Println(u, i)
// fmt.Println(u, i)
}
if cnt != waitCnt*loopCnt {
t.Fatal("总数不对")
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ XMM 测试程序快速预览下载使用:
3. [XMM 使用 - 链表](https://github.com/heiyeluren/XMM/blob/main/example/xmm-test02.go)
4. [XMM 使用 - 哈希表](https://github.com/heiyeluren/XMM/blob/main/example/xmm-test03.go)


<br />
<br />

## XMM 实现原理介绍

### 1. [XMM 的核心设计与实现流程](https://github.com/heiyeluren/XMM/blob/main/docs/XMM-DesignImplementation.md)

### 2. [XMM 设计实现技术调研参考](https://github.com/heiyeluren/XMM/blob/main/docs/XMM-InvestigateResearch.md)

<br />
Expand Down
Loading