Files
monibuca/pkg/util/mem.go
2024-06-19 17:33:54 +08:00

246 lines
5.5 KiB
Go

package util
import (
"container/list"
"fmt"
"slices"
"sync"
"unsafe"
)
const (
MaxBlockSize = 1 << 22
BuddySize = MaxBlockSize << 7
MinPowerOf2 = 10
)
var (
memoryPool [BuddySize]byte
buddy = NewBuddy(BuddySize >> MinPowerOf2)
lock sync.Mutex
poolStart = int64(uintptr(unsafe.Pointer(&memoryPool[0])))
blockPool = list.New()
EnableCheckSize bool = false
)
type MemoryAllocator struct {
allocator *Allocator
start int64
memory []byte
Size int
}
func GetMemoryAllocator(size int) (ret *MemoryAllocator) {
lock.Lock()
offset, err := buddy.Alloc(size >> MinPowerOf2)
if blockPool.Len() > 0 {
ret = blockPool.Remove(blockPool.Front()).(*MemoryAllocator)
} else {
ret = &MemoryAllocator{
allocator: NewAllocator(size),
}
}
lock.Unlock()
ret.Size = size
ret.allocator.Init(size)
if err != nil {
ret.memory = make([]byte, size)
ret.start = int64(uintptr(unsafe.Pointer(&ret.memory[0])))
return
}
offset = offset << MinPowerOf2
ret.memory = memoryPool[offset : offset+size]
ret.start = poolStart + int64(offset)
return
}
func (ma *MemoryAllocator) Recycle() {
ma.allocator.Recycle()
lock.Lock()
blockPool.PushBack(ma)
_ = buddy.Free(int((poolStart - ma.start) >> MinPowerOf2))
ma.memory = nil
lock.Unlock()
}
func (ma *MemoryAllocator) Malloc(size int) (memory []byte) {
if offset := ma.allocator.Allocate(size); offset != -1 {
memory = ma.memory[offset : offset+size]
}
return
}
func (ma *MemoryAllocator) free(start, size int) (ret bool) {
if start < 0 || start+size > ma.Size {
return
}
ma.allocator.Free(start, size)
return true
}
func (ma *MemoryAllocator) Free(mem []byte) bool {
start := int(int64(uintptr(unsafe.Pointer(&mem[0]))) - ma.start)
return ma.free(start, len(mem))
}
func (ma *MemoryAllocator) GetBlocks() (blocks []*Block) {
return ma.allocator.GetBlocks()
}
type ScalableMemoryAllocator struct {
children []*MemoryAllocator
totalMalloc int64
totalFree int64
size int
childSize int
}
func NewScalableMemoryAllocator(size int) (ret *ScalableMemoryAllocator) {
return &ScalableMemoryAllocator{children: []*MemoryAllocator{GetMemoryAllocator(size)}, size: size, childSize: size}
}
func (sma *ScalableMemoryAllocator) checkSize() {
var totalFree int
for _, child := range sma.children {
totalFree += child.allocator.GetFreeSize()
}
if inUse := sma.totalMalloc - sma.totalFree; totalFree != sma.size-int(inUse) {
panic("CheckSize")
} else {
if inUse > 3000000 {
fmt.Println(uintptr(unsafe.Pointer(sma)), inUse)
}
}
}
func (sma *ScalableMemoryAllocator) addMallocCount(size int) {
sma.totalMalloc += int64(size)
}
func (sma *ScalableMemoryAllocator) addFreeCount(size int) {
sma.totalFree += int64(size)
}
func (sma *ScalableMemoryAllocator) GetTotalMalloc() int64 {
return sma.totalMalloc
}
func (sma *ScalableMemoryAllocator) GetTotalFree() int64 {
return sma.totalFree
}
func (sma *ScalableMemoryAllocator) GetChildren() []*MemoryAllocator {
return sma.children
}
func (sma *ScalableMemoryAllocator) Recycle() {
for _, child := range sma.children {
child.Recycle()
}
}
func (sma *ScalableMemoryAllocator) Malloc(size int) (memory []byte) {
if sma == nil || size > MaxBlockSize {
return
}
if EnableCheckSize {
defer sma.checkSize()
}
defer sma.addMallocCount(size)
var child *MemoryAllocator
for _, child = range sma.children {
if memory = child.Malloc(size); memory != nil {
return
}
}
for sma.childSize < MaxBlockSize {
sma.childSize = sma.childSize << 1
if sma.childSize >= size {
break
}
}
child = GetMemoryAllocator(sma.childSize)
sma.size += child.Size
memory = child.Malloc(size)
sma.children = append(sma.children, child)
return
}
func (sma *ScalableMemoryAllocator) GetScalableMemoryAllocator() *ScalableMemoryAllocator {
return sma
}
func (sma *ScalableMemoryAllocator) Free(mem []byte) bool {
if sma == nil {
return false
}
if EnableCheckSize {
defer sma.checkSize()
}
ptr := int64(uintptr(unsafe.Pointer(&mem[0])))
size := len(mem)
for i, child := range sma.children {
if start := int(ptr - child.start); start >= 0 && start < child.Size && child.free(start, size) {
sma.addFreeCount(size)
if len(sma.children) > 1 && child.allocator.sizeTree.End-child.allocator.sizeTree.Start == child.Size {
child.Recycle()
sma.children = slices.Delete(sma.children, i, i+1)
sma.size -= child.Size
}
return true
}
}
return false
}
type RecyclableMemory struct {
*ScalableMemoryAllocator
Memory
RecycleIndexes []int
}
func (r *RecyclableMemory) NextN(size int) (memory []byte) {
memory = r.ScalableMemoryAllocator.Malloc(size)
if memory == nil {
memory = make([]byte, size)
} else if r.RecycleIndexes != nil {
r.RecycleIndexes = append(r.RecycleIndexes, r.Count())
}
r.Append(memory)
return
}
func (r *RecyclableMemory) AddRecycleBytes(b []byte) {
if r.RecycleIndexes != nil {
r.RecycleIndexes = append(r.RecycleIndexes, r.Count())
}
r.Append(b)
}
func (r *RecyclableMemory) RemoveRecycleBytes(index int) (buf []byte) {
if index < 0 {
index = r.Count() + index
}
buf = r.Buffers[index]
if r.RecycleIndexes != nil {
i := slices.Index(r.RecycleIndexes, index)
r.RecycleIndexes = slices.Delete(r.RecycleIndexes, i, i+1)
}
r.Buffers = slices.Delete(r.Buffers, index, index+1)
r.Size -= len(buf)
return
}
func (r *RecyclableMemory) Recycle() {
if r.RecycleIndexes != nil {
for _, index := range r.RecycleIndexes {
r.Free(r.Buffers[index])
}
r.RecycleIndexes = r.RecycleIndexes[:0]
} else {
for _, buf := range r.Buffers {
r.Free(buf)
}
}
}