github.com/userpro/linearpool@v0.5.3-0.20231115092206-0ca073169b71/memorypool.go (about)

     1  package memorypool
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  	"unsafe"
     8  )
     9  
    10  const (
    11  	DiKB             int64 = 1 << 10
    12  	DiMB             int64 = DiKB << 10
    13  	DiGB             int64 = DiMB << 10
    14  	defaultBlockSize int64 = DiKB * 4 // 4KB/block
    15  )
    16  
    17  var (
    18  	SliceExtendRatio        = 2.5
    19  	BugfixClearPointerInMem = true
    20  	BugfixCorruptOtherMem   = true
    21  
    22  	allocatorPool = sync.Pool{
    23  		New: func() any {
    24  			return &Allocator{bidx: -1}
    25  		},
    26  	}
    27  )
    28  
    29  // Allocator 分配器
    30  type Allocator struct {
    31  	blockSize  int64
    32  	curBlock   *sliceHeader
    33  	blocks     []*sliceHeader
    34  	hugeBlocks []*sliceHeader
    35  	bidx       int // 当前在第几个 block 进行分配
    36  
    37  	externalPtr    []unsafe.Pointer
    38  	externalSlice  []unsafe.Pointer
    39  	externalString []unsafe.Pointer
    40  	externalMap    []any
    41  	externalFunc   []any
    42  
    43  	subAlloctor []*Allocator // 子分配器
    44  }
    45  
    46  // NewAlloctorFromPool 新建分配池, blocksize >= bsize
    47  func NewAlloctorFromPool(bsize int64) *Allocator {
    48  	ac := allocatorPool.Get().(*Allocator)
    49  	if bsize <= 0 {
    50  		bsize = defaultBlockSize
    51  	}
    52  
    53  	if ac.bidx < 0 { // 新建 alloctor
    54  		ac.blockSize = bsize
    55  		ac.newBlock()
    56  	} else if ac.blockSize < bsize { // 如果准备复用的 blocksize 小于所需要的, 则需要重新分配
    57  		ac.clearBlock()
    58  		ac.blockSize = bsize
    59  		ac.newBlock()
    60  	}
    61  	return ac
    62  }
    63  
    64  func (ac *Allocator) newBlockWithSz(need int64) *sliceHeader {
    65  	t := make([]byte, 0, need)
    66  	b := (*sliceHeader)(unsafe.Pointer(&t))
    67  	ac.hugeBlocks = append(ac.hugeBlocks, b)
    68  	return b
    69  }
    70  
    71  func (ac *Allocator) newBlock() *sliceHeader {
    72  	ac.bidx++
    73  	// 可能复用之前的blocks
    74  	if len(ac.blocks) > ac.bidx {
    75  		b := ac.blocks[ac.bidx]
    76  		ac.curBlock = b
    77  		return b
    78  	}
    79  
    80  	t := make([]byte, 0, ac.blockSize)
    81  	b := (*sliceHeader)(unsafe.Pointer(&t))
    82  	ac.curBlock = b
    83  	ac.blocks = append(ac.blocks, b)
    84  	return b
    85  }
    86  
    87  func (ac *Allocator) clearBlock() {
    88  	ac.curBlock = nil
    89  	ac.blocks = nil
    90  }
    91  
    92  func (ac *Allocator) alloc(need int64) unsafe.Pointer {
    93  	if need == 0 && BugfixCorruptOtherMem {
    94  		return nil
    95  	}
    96  
    97  	// round up
    98  	needAligned := need
    99  	if need%ptrSize != 0 {
   100  		needAligned = (need + ptrSize + 1) & ^(ptrSize - 1)
   101  	}
   102  
   103  	// 分配小型对象
   104  	if ac.blockSize >= needAligned {
   105  		b := ac.curBlock
   106  		if b.Len+int64(needAligned) > b.Cap {
   107  			b = ac.newBlock()
   108  		}
   109  
   110  		ptr := unsafe.Add(b.Data, b.Len)
   111  		b.Len += needAligned
   112  		// fmt.Printf("bidx: %d, blocksize: %d, alloc need: %d, needAligned: %d, len: %d, %v - %v\n",
   113  		// 	ac.bidx, len(ac.blocks), need, needAligned, b.Len, ptr, unsafe.Add(b.Data, b.Cap-1))
   114  		return ptr
   115  	}
   116  
   117  	// 分配巨型对象
   118  	b := ac.newBlockWithSz(needAligned)
   119  	ptr := b.Data
   120  	b.Len = b.Cap
   121  	// fmt.Printf("huge alloc need: %d, needAligned: %d, cap: %d, %v - %v\n",
   122  	// 	need, needAligned, b.Cap, b.Data, unsafe.Add(b.Data, b.Cap-1))
   123  	return ptr
   124  }
   125  
   126  // Reset 重置内存信息
   127  func (ac *Allocator) Reset() {
   128  	ac.bidx = 0
   129  	ac.curBlock = ac.blocks[0]
   130  	for _, b := range ac.blocks {
   131  		if b.Len > 0 {
   132  			memclrNoHeapPointers(b.Data, uintptr(b.Len))
   133  			b.Len = 0
   134  		}
   135  	}
   136  	ac.blocks = ac.blocks[:1]
   137  	ac.hugeBlocks = nil // 大对象直接释放 避免过多占用内存
   138  
   139  	ac.externalPtr = ac.externalPtr[:0]
   140  	ac.externalSlice = ac.externalSlice[:0]
   141  	ac.externalString = ac.externalString[:0]
   142  	ac.externalMap = ac.externalMap[:0]
   143  	ac.externalFunc = ac.externalFunc[:0]
   144  
   145  	for i, subAc := range ac.subAlloctor {
   146  		subAc.Reset()
   147  		ac.subAlloctor[i] = nil
   148  	}
   149  	ac.subAlloctor = ac.subAlloctor[:0]
   150  }
   151  
   152  // ReturnAlloctorToPool 归还分配池
   153  func (ac *Allocator) ReturnAlloctorToPool() {
   154  	ac.Reset()
   155  	allocatorPool.Put(ac)
   156  }
   157  
   158  // BlockSize 获取当前内存池的 blocksize
   159  func (ac *Allocator) BlockSize() int64 {
   160  	return ac.blockSize
   161  }
   162  
   163  // AddSubAlloctor 新增子分配器
   164  func (ac *Allocator) AddSubAlloctor(sub *Allocator) {
   165  	ac.subAlloctor = append(ac.subAlloctor, sub)
   166  }
   167  
   168  // SubAlloctor 获取子分配器
   169  func (ac *Allocator) SubAlloctor() []*Allocator {
   170  	return ac.subAlloctor
   171  }
   172  
   173  // Merge 合并其他内存池
   174  func (ac *Allocator) Merge(src *Allocator) *Allocator {
   175  	ac.blocks = append(ac.blocks, src.blocks[:src.bidx+1]...)
   176  	ac.hugeBlocks = append(ac.hugeBlocks, src.hugeBlocks...)
   177  	ac.bidx = ac.bidx + src.bidx + 1
   178  
   179  	ac.externalPtr = append(ac.externalPtr, src.externalPtr...)
   180  	ac.externalSlice = append(ac.externalSlice, src.externalSlice...)
   181  	ac.externalString = append(ac.externalString, src.externalString...)
   182  	ac.externalMap = append(ac.externalMap, src.externalMap...)
   183  	ac.externalFunc = append(ac.externalFunc, src.externalFunc...)
   184  	return ac
   185  }
   186  
   187  // KeepAlive GC保活
   188  func (ac *Allocator) KeepAlive(ptr interface{}) {
   189  	d := data(ptr)
   190  	if d == nil {
   191  		return
   192  	}
   193  
   194  	k := reflect.TypeOf(ptr).Kind()
   195  	switch k {
   196  	case reflect.Ptr:
   197  		ac.externalPtr = append(ac.externalPtr, d)
   198  	case reflect.Slice:
   199  		ac.externalSlice = append(ac.externalSlice, (*sliceHeader)(d).Data)
   200  	case reflect.String:
   201  		ac.externalString = append(ac.externalString, (*stringHeader)(d).Data)
   202  	case reflect.Map:
   203  		ac.externalMap = append(ac.externalMap, d)
   204  	case reflect.Func:
   205  		ac.externalFunc = append(ac.externalFunc, ptr)
   206  	default:
   207  		panic(fmt.Errorf("unsupported type: %v", k))
   208  	}
   209  }
   210  
   211  // New 分配新对象
   212  func New[T any](ac *Allocator) (r *T) {
   213  	r = (*T)(ac.alloc(int64(unsafe.Sizeof(*r))))
   214  	return r
   215  }
   216  
   217  // NewSlice does not zero the slice automatically, this is OK with most cases and can improve the performance.
   218  // zero it yourself for your need.
   219  func NewSlice[T any](ac *Allocator, len, cap int) (r []T) {
   220  	// keep same with systems `new`.
   221  	if len > cap {
   222  		panic("NewSlice: cap out of range")
   223  	}
   224  
   225  	if BugfixCorruptOtherMem && cap == 0 {
   226  	}
   227  
   228  	slice := (*sliceHeader)(unsafe.Pointer(&r))
   229  	var t T
   230  	slice.Data = ac.alloc(int64(cap) * int64(unsafe.Sizeof(t)))
   231  	slice.Len = int64(len)
   232  	slice.Cap = int64(cap)
   233  	return r
   234  }
   235  
   236  // AppendMulti append slice
   237  func AppendMulti[T any](ac *Allocator, s []T, elems ...T) []T {
   238  	if len(elems) == 0 {
   239  		return s
   240  	}
   241  
   242  	h := (*sliceHeader)(unsafe.Pointer(&s))
   243  	elemSz := int(unsafe.Sizeof(elems[0]))
   244  	src := (*sliceHeader)(unsafe.Pointer(&elems))
   245  
   246  	// grow
   247  	if h.Len+src.Len > h.Cap {
   248  		pre := *h
   249  		h.Cap = int64(roundupsize(uintptr(pre.Cap + int64(len(elems)))))
   250  		sz := int(h.Cap) * elemSz
   251  		h.Data = ac.alloc(int64(sz))
   252  		memmoveNoHeapPointers(h.Data, pre.Data, uintptr(int(pre.Len)*elemSz))
   253  	}
   254  
   255  	// append
   256  	memmoveNoHeapPointers(unsafe.Add(h.Data, elemSz*int(h.Len)), src.Data, uintptr(elemSz*int(src.Len)))
   257  	h.Len += src.Len
   258  
   259  	return s
   260  }
   261  
   262  // Append append slice
   263  func Append[T any](ac *Allocator, s []T, elem T) []T {
   264  	h := (*sliceHeader)(unsafe.Pointer(&s))
   265  	elemSz := int(unsafe.Sizeof(elem))
   266  
   267  	// grow
   268  	if h.Len+1 > h.Cap {
   269  		pre := *h
   270  		h.Cap = int64(roundupsize(uintptr(pre.Cap + 1)))
   271  		sz := int(h.Cap) * elemSz
   272  		h.Data = ac.alloc(int64(sz))
   273  		memmoveNoHeapPointers(h.Data, pre.Data, uintptr(int(pre.Len)*elemSz))
   274  	}
   275  
   276  	// append
   277  	*(*T)(unsafe.Add(h.Data, elemSz*int(h.Len))) = elem
   278  	h.Len += 1
   279  
   280  	return s
   281  }
   282  
   283  // AppendInplaceMulti 与 NewSlice 之间没有任何新的内存分配时使用
   284  func AppendInplaceMulti[T any](ac *Allocator, s []T, elems ...T) []T {
   285  	if len(elems) == 0 {
   286  		return s
   287  	}
   288  
   289  	h := (*sliceHeader)(unsafe.Pointer(&s))
   290  	elemSz := int(unsafe.Sizeof(elems[0]))
   291  	src := (*sliceHeader)(unsafe.Pointer(&elems))
   292  
   293  	// grow
   294  	if h.Len+src.Len > h.Cap {
   295  		pre := *h
   296  		newcap := int64(roundupsize(uintptr(pre.Cap + int64(len(elems)))))
   297  		curB := ac.curBlock
   298  		growthInplace := (newcap - pre.Cap) * int64(elemSz)
   299  		if curB.Len+growthInplace < curB.Cap {
   300  			curB.Len += growthInplace
   301  		} else {
   302  			sz := int(newcap) * elemSz
   303  			h.Data = ac.alloc(int64(sz))
   304  			memmoveNoHeapPointers(h.Data, pre.Data, uintptr(int(pre.Len)*elemSz))
   305  		}
   306  		h.Cap = newcap
   307  	}
   308  
   309  	// append
   310  	memmoveNoHeapPointers(unsafe.Add(h.Data, elemSz*int(h.Len)), src.Data, uintptr(elemSz*int(src.Len)))
   311  	h.Len += src.Len
   312  
   313  	return s
   314  }
   315  
   316  // AppendInplace 与 NewSlice 之间没有任何新的内存分配时使用
   317  func AppendInplace[T any](ac *Allocator, s []T, elem T) []T {
   318  	h := (*sliceHeader)(unsafe.Pointer(&s))
   319  	elemSz := int(unsafe.Sizeof(elem))
   320  
   321  	// grow
   322  	if h.Len+1 > h.Cap {
   323  		pre := *h
   324  		newcap := int64(roundupsize(uintptr(pre.Cap + 1)))
   325  		if ac.curBlock.Len+newcap-h.Cap < ac.curBlock.Cap {
   326  			ac.curBlock.Len += newcap - h.Cap
   327  		} else {
   328  			sz := int(newcap) * elemSz
   329  			h.Data = ac.alloc(int64(sz))
   330  			memmoveNoHeapPointers(h.Data, pre.Data, uintptr(int(pre.Len)*elemSz))
   331  		}
   332  		h.Cap = newcap
   333  	}
   334  
   335  	// append
   336  	*(*T)(unsafe.Add(h.Data, elemSz*int(h.Len))) = elem
   337  	h.Len += 1
   338  
   339  	return s
   340  }
   341  
   342  // AppendInbound 确定范围内 append
   343  func AppendInbound[T any](ac *Allocator, s []T, elem T) []T {
   344  	if len(s)+1 > cap(s) {
   345  		panic(fmt.Sprintf("index %d out of bound cap %d", len(s), cap(s)))
   346  	}
   347  
   348  	return append(s, elem)
   349  }
   350  
   351  // NewString 从内存池分配 string
   352  func (ac *Allocator) NewString(v string) string {
   353  	if len(v) == 0 {
   354  		return ""
   355  	}
   356  	h := (*stringHeader)(unsafe.Pointer(&v))
   357  	ptr := ac.alloc(int64(h.Len))
   358  	if ptr != nil {
   359  		memmoveNoHeapPointers(ptr, h.Data, uintptr(h.Len))
   360  	}
   361  	h.Data = ptr
   362  	return v
   363  }
   364  
   365  // Debug 输出 debug 信息
   366  func (ac *Allocator) Debug() {
   367  	fmt.Printf("\n* bidx: %d\n", ac.bidx)
   368  	fmt.Printf("* blocks: \n")
   369  	fmt.Printf(" - curblock: len(%d) cap(%d) addr[%p - %p]\n", ac.curBlock.Len, ac.curBlock.Cap, ac.curBlock.Data, unsafe.Add(ac.curBlock.Data, ac.curBlock.Cap-1))
   370  	for i, b := range ac.blocks {
   371  		b1 := *(*[]byte)(unsafe.Pointer(b))
   372  		fmt.Printf(" - b[%d]: len(%d) cap(%d) addr[%p - %p] data: %v\n", i, b.Len, b.Cap, b.Data, unsafe.Add(b.Data, b.Cap-1), b1)
   373  	}
   374  
   375  	if len(ac.hugeBlocks) > 0 {
   376  		fmt.Printf("* huge blocks: \n")
   377  		for i, b := range ac.hugeBlocks {
   378  			b1 := *(*[]byte)(unsafe.Pointer(b))
   379  			fmt.Printf(" - hb[%d]: len(%d) cap(%d) addr[%p - %p] data: %v\n", i, b.Len, b.Cap, b.Data, unsafe.Add(b.Data, b.Cap-1), b1)
   380  		}
   381  	}
   382  	fmt.Printf("\n")
   383  }
   384  
   385  //============================================================================
   386  // Protobuf2 APIs
   387  //============================================================================
   388  
   389  // Bool ...
   390  func (ac *Allocator) Bool(v bool) (r *bool) {
   391  	r = (*bool)(ac.alloc(int64(unsafe.Sizeof(v))))
   392  	*r = v
   393  	return
   394  }
   395  
   396  // Int ...
   397  func (ac *Allocator) Int(v int) (r *int) {
   398  	r = (*int)(ac.alloc(int64(unsafe.Sizeof(v))))
   399  	*r = v
   400  	return
   401  }
   402  
   403  // Int32 ...
   404  func (ac *Allocator) Int32(v int32) (r *int32) {
   405  	r = (*int32)(ac.alloc(int64(unsafe.Sizeof(v))))
   406  	*r = v
   407  	return
   408  }
   409  
   410  // Uint32 ...
   411  func (ac *Allocator) Uint32(v uint32) (r *uint32) {
   412  	r = (*uint32)(ac.alloc(int64(unsafe.Sizeof(v))))
   413  	*r = v
   414  	return
   415  }
   416  
   417  // Int64 ...
   418  func (ac *Allocator) Int64(v int64) (r *int64) {
   419  	r = (*int64)(ac.alloc(int64(unsafe.Sizeof(v))))
   420  	*r = v
   421  	return
   422  }
   423  
   424  // Uint64 ...
   425  func (ac *Allocator) Uint64(v uint64) (r *uint64) {
   426  	r = (*uint64)(ac.alloc(int64(unsafe.Sizeof(v))))
   427  	*r = v
   428  	return
   429  }
   430  
   431  // Float32 ...
   432  func (ac *Allocator) Float32(v float32) (r *float32) {
   433  	r = (*float32)(ac.alloc(int64(unsafe.Sizeof(v))))
   434  	*r = v
   435  	return
   436  }
   437  
   438  // Float64 ...
   439  func (ac *Allocator) Float64(v float64) (r *float64) {
   440  	r = (*float64)(ac.alloc(int64(unsafe.Sizeof(v))))
   441  	*r = v
   442  	return
   443  }
   444  
   445  // String ...
   446  func (ac *Allocator) String(v string) (r *string) {
   447  	r = (*string)(ac.alloc(int64(unsafe.Sizeof(v))))
   448  	*r = ac.NewString(v)
   449  	return
   450  }