From 0bb4a96a112b0c4cbd67beb9ea6db4867b6ffd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=91=E5=A4=9C=E8=B7=AF=E4=BA=BA?= Date: Thu, 17 Feb 2022 12:19:37 +0800 Subject: [PATCH] Delete src directory --- src/bit_map.go | 212 ---------- src/bit_map_test.go | 80 ---- src/chunk.go | 25 -- src/class_span.go | 138 ------- src/common.go | 191 --------- src/common_test.go | 58 --- src/entry.go | 808 --------------------------------------- src/heap.go | 520 ------------------------- src/linear_alloc.go | 197 ---------- src/linear_alloc_test.go | 140 ------- src/metadata.go | 80 ---- src/metadata_alloc.go | 240 ------------ src/raw_linear_memory.go | 26 -- src/raw_memory.go | 159 -------- src/raw_memory_test.go | 186 --------- src/span.go | 283 -------------- src/span_pool.go | 328 ---------------- src/span_pool_test.go | 176 --------- src/span_test.go | 79 ---- src/string_alloc.go | 122 ------ src/trees.go | 472 ----------------------- src/xmm.go | 175 --------- src/xmm_test.go | 720 ---------------------------------- 23 files changed, 5415 deletions(-) delete mode 100644 src/bit_map.go delete mode 100644 src/bit_map_test.go delete mode 100644 src/chunk.go delete mode 100644 src/class_span.go delete mode 100644 src/common.go delete mode 100644 src/common_test.go delete mode 100644 src/entry.go delete mode 100644 src/heap.go delete mode 100644 src/linear_alloc.go delete mode 100644 src/linear_alloc_test.go delete mode 100644 src/metadata.go delete mode 100644 src/metadata_alloc.go delete mode 100644 src/raw_linear_memory.go delete mode 100644 src/raw_memory.go delete mode 100644 src/raw_memory_test.go delete mode 100644 src/span.go delete mode 100644 src/span_pool.go delete mode 100644 src/span_pool_test.go delete mode 100644 src/span_test.go delete mode 100644 src/string_alloc.go delete mode 100644 src/trees.go delete mode 100644 src/xmm.go delete mode 100644 src/xmm_test.go diff --git a/src/bit_map.go b/src/bit_map.go deleted file mode 100644 index 6d5c918..0000000 --- a/src/bit_map.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "fmt" - "log" - "reflect" - "sync/atomic" - "unsafe" -) - -type gcBits uint32 - -func newMarkBits(nelems uintptr, zero bool) (*gcBits, error) { - blocksNeeded := (nelems + 63) / 64 - uint32Needed := blocksNeeded * 2 - allocator := newXAllocator(4 * uint32Needed) - p, err := allocator.alloc() - if err != nil { - return nil, err - } - bits := (*gcBits)(p) - if zero { - return bits, nil - } - for i := 0; i < int(uint32Needed); i++ { - uint32p := bits.uint32p(uintptr(i)) - atomic.StoreUint32(uint32p, ^*uint32p) - } - return bits, nil -} - -func newAllocBits(nelems uintptr) (*gcBits, error) { - return newMarkBits(nelems, false) -} - -// uint32p returns a pointer to the n'th byte of b. -func (b *gcBits) uint32p(n uintptr) *uint32 { - return addb((*uint32)(b), n*4) -} - -func (b *gcBits) invert(len uintptr) { - num := len/32 + 1 - if len%32 == 0 { - num -= 1 - } - for i := 0; i < int(num); i++ { - uint32p := b.uint32p(uintptr(i)) - atomic.StoreUint32(uint32p, ^*uint32p) - } -} -func (b *gcBits) show32(len uintptr) { - num := len/32 + 1 - if len%32 == 0 || len <= 32 { - num -= 1 - } - ss := "" - for i := 0; i < int(num); i++ { - uint32p := b.uint32p(uintptr(i)) - ss += fmt.Sprintf("%.32b ", *uint32p) - } - log.Println(ss) -} - -func (b *gcBits) show64(len uintptr) { - num := len/64 + 1 - if len%64 == 0 || len <= 64 { - num -= 1 - } - ss := "" - for i := 0; i < int(num); i++ { - bytes := (*[8]uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(b)) + uintptr(8*i))) - aCache := uint64(0) - aCache |= uint64(bytes[0]) - aCache |= uint64(bytes[1]) << (1 * 8) - aCache |= uint64(bytes[2]) << (2 * 8) - aCache |= uint64(bytes[3]) << (3 * 8) - aCache |= uint64(bytes[4]) << (4 * 8) - aCache |= uint64(bytes[5]) << (5 * 8) - aCache |= uint64(bytes[6]) << (6 * 8) - aCache |= uint64(bytes[7]) << (7 * 8) - ss += fmt.Sprintf("%.64b ", aCache) - } - log.Println(ss) -} - -func String(b *gcBits, nelems uintptr) (res string) { - sh := reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(b)), - Len: int(nelems), - Cap: int(nelems), - } - gcBitss := *(*[]gcBits)(unsafe.Pointer(&sh)) - for _, bitss := range gcBitss { - item := fmt.Sprintf("%.32b", bitss) - res += item - } - return res -} - -func addb(p *uint32, n uintptr) *uint32 { - // Note: wrote out full expression instead of calling add(p, n) - // to reduce the number of temporaries generated by the - // compiler for this trivial expression during inlining. - return (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + n)) -} - -// bitp returns a pointer to the byte containing bit n and a mask for -// selecting that bit from *uint32p. -func (b *gcBits) bitp(n uintptr) (uint32p *uint32, mask uint32) { - return b.uint32p(n / 32), 1 << (n % 32) -} - -func markBitsForAddr(p uintptr, h *xHeap) error { - s, err := h.spanOf(p) - if err != nil { - return err - } - objIndex := s.objIndex(p) - s.setMarkBitsForIndex(objIndex) - if logg { - fmt.Println("markBitsForAddr", uintptr(unsafe.Pointer(s)), objIndex) - } - return nil -} - -// isMarked reports whether mark bit m is set. -func (m markBits) isMarked() bool { - return *m.uint32p&m.mask != 0 -} - -// setMarked sets the marked bit in the markbits, atomically. -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) - for { - val := atomic.LoadUint32(m.uint32p) - if atomic.CompareAndSwapUint32(m.uint32p, val, val^m.mask) { - return - } - } - //atomic.StoreUint32(m.uint32p, *m.uint32p^m.mask) -} - -// setMarkedNonAtomic sets the marked bit in the markbits, non-atomically. -func (m markBits) setMarkedNonAtomic() { - *m.uint32p |= m.mask -} - -// clearMarked clears the marked bit in the markbits, atomically. -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) - for { - val := atomic.LoadUint32(m.uint32p) - if atomic.CompareAndSwapUint32(m.uint32p, val, val&(^m.mask)) { - return - } - } - //atomic.StoreUint32(m.uint32p, *m.uint32p&(^m.mask)) -} - -// markBitsForSpan returns the markBits for the span base address base. -func markBitsForSpan(base uintptr, h *xHeap) (mbits markBits, err error) { - err = markBitsForAddr(base, h) - if err != nil { - return markBits{}, err - } - if mbits.mask != 1 { - return mbits, errors.New("markBitsForSpan: unaligned start") - } - return mbits, nil -} - -// advance advances the markBits to the next object in the span. -func (m *markBits) advance() { - if m.mask == 1<<7 { - m.uint32p = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(m.uint32p)) + 1)) - m.mask = 1 - } else { - m.mask = m.mask << 1 - } - m.index++ -} - -type markBits struct { - uint32p *uint32 - mask uint32 - index uintptr -} diff --git a/src/bit_map_test.go b/src/bit_map_test.go deleted file mode 100644 index 6e14b53..0000000 --- a/src/bit_map_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "testing" - "unsafe" -) - -func TestMarkBits(t *testing.T) { - num, objIndex := 68, uintptr(10) - bit, err := newMarkBits(uintptr(num), true) - if err != nil { - t.Fatal(err) - } - //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)) - 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)) - bytep, mask = bit.bitp(uintptr(5000000)) - mb = markBits{bytep, mask, objIndex} - - fmt.Printf("bytep:%.32b , mask:%.32b isMarked:%t \n", *bytep, mask, mb.isMarked()) -} - -func TestMarkBits2(t *testing.T) { - num, objIndex := 80, uintptr(78) - bit, err := newMarkBits(uintptr(num), true) - if err != nil { - t.Fatal(err) - } - 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() - - bytep, mask = bit.bitp(objIndex) - ss := refillAllocCache(78/32, bit) - fmt.Printf("AllocCache:%.64b , bytep:%.32b \n", ss, *(bytep)) -} - -func refillAllocCache(whichByte uintptr, allocBits *gcBits) uint64 { - bytes := (*[8]uint8)(unsafe.Pointer(allocBits.uint32p(whichByte))) - aCache := uint64(0) - aCache |= uint64(bytes[0]) - aCache |= uint64(bytes[1]) << (1 * 8) - aCache |= uint64(bytes[2]) << (2 * 8) - aCache |= uint64(bytes[3]) << (3 * 8) - aCache |= uint64(bytes[4]) << (4 * 8) - aCache |= uint64(bytes[5]) << (5 * 8) - aCache |= uint64(bytes[6]) << (6 * 8) - aCache |= uint64(bytes[7]) << (7 * 8) - return ^aCache -} diff --git a/src/chunk.go b/src/chunk.go deleted file mode 100644 index ec8d3bd..0000000 --- a/src/chunk.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -//连续page的管理 -type xChunk struct { - startAddr uintptr - npages uintptr -} diff --git a/src/class_span.go b/src/class_span.go deleted file mode 100644 index 5364cba..0000000 --- a/src/class_span.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" -) - -type xClassSpan struct { - - //lock sync.Mutex - - classIndex uint // class的索引 - - //空的, - free *mSpanList - - //满的span。 - full *mSpanList - - // nmalloc is the cumulative count of objects allocated from - // this mcentral, assuming all spans in mcaches are - // fully-allocated. Written atomically, read under STW. - nmalloc uint64 - - heap *xHeap -} - -func (x *xClassSpan) Init(classIndex uint, heap *xHeap) error { - if heap == nil { - return errors.New("heap is nil") - } - x.classIndex = classIndex - x.heap = heap - x.free = &mSpanList{} - x.full = &mSpanList{} - return nil -} - -func (x *xClassSpan) allocSpan(index int, f float32) (*xSpan, error) { - if x.free.first != nil { - span, err := func() (*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)) - return span, nil - } - } - heap := x.heap - pageNum := class_to_allocnpages[index] - 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)) - if err != nil { - return nil, err - } - return span, nil -} - -func (x *xClassSpan) releaseSpan(span *xSpan) { - /*if logg || span.classIndex == 6 { - fmt.Println("releaseSpan", uintptr(unsafe.Pointer(span))) - }*/ - x.full.insert(span) -} - -/** - 释放span,只能释放full中的 -*/ -func (x *xClassSpan) freeSpan(span *xSpan) (swap bool, size uint, err error) { - if span == nil { - return false, 0, errors.New("span is nil") - } - needFree := false - err = func() error { - gcCount := span.countGcMarkBits() - //没有达到gc阈值或者当前span正在被分配不做GC(异步gc该类span) - if gcCount <= uintptr(float64(span.nelems)*SpanGCFactor) { - return nil - } - if span.allocCount < span.nelems { - return nil - } - span.lock.Lock() - defer span.lock.Unlock() - allocCount := span.allocCount - gcCount = span.countGcMarkBits() - x.heap.addFreeCapacity(-int64(allocCount)) - //没有达到gc阈值或者当前span正在被分配不做GC(异步gc该类span) - if gcCount < uintptr(float64(span.nelems)*SpanGCFactor) { - return nil - } - if span.allocCount < span.nelems { - return nil - } - size = uint(gcCount * span.classSize) - needFree = true - span.freeIndex = 0 - span.allocCount = span.nelems - gcCount - //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) - return nil - }() - if err != nil { - return false, 0, err - } - if !needFree { - return false, 0, nil - } - //判断当前span是否不在使用,不在使用存放进去。在使用则 - //log.Printf("xClassSpan class:%d 回收 span:%d\n", x.classIndex, unsafe.Pointer(span)) - x.free.insert(span) - return true, size, nil -} diff --git a/src/common.go b/src/common.go deleted file mode 100644 index b3499d3..0000000 --- a/src/common.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "github.com/spf13/cast" - "log" - "runtime/debug" - "sync" - "sync/atomic" - "unsafe" -) - -func Recover() { - if rec := recover(); rec != nil { - if err, ok := rec.(error); ok { - log.Printf("PanicRecover Unhandled error: %v\n stack:%v \n", err.Error(), cast.ToString(debug.Stack())) - } else { - log.Printf("PanicRecover Panic: %v\n stack:%v \n", rec, cast.ToString(debug.Stack())) - } - } -} - -// align returns the smallest y >= x such that y % a == 0. -func Align(x, a uintptr) uintptr { - if a < 1 { - return x - } - y := x + a - 1 - return y - y%a -} - -func round(n, a uintptr) uintptr { - return (n + a - 1) &^ (a - 1) -} - -const deBruijn64 = 0x0218a392cd3d5dbf - -var deBruijnIdx64 = [64]byte{ - 0, 1, 2, 7, 3, 13, 8, 19, - 4, 25, 14, 28, 9, 34, 20, 40, - 5, 17, 26, 38, 15, 46, 29, 48, - 10, 31, 35, 54, 21, 50, 41, 57, - 63, 6, 12, 18, 24, 27, 33, 39, - 16, 37, 45, 47, 30, 53, 49, 56, - 62, 11, 23, 32, 36, 44, 52, 55, - 61, 22, 43, 51, 60, 42, 59, 58, -} - -// Ctz64 counts trailing (low-order) zeroes, -// and if all are zero, then 64. -// 找到最后的1在第几个bit位. https://blog.csdn.net/cyq6239075/article/details/106412814 -func Ctz64(x uint64) int { - x &= -x // isolate low-order bit,计算出最后一个bit对应数字 - y := x * deBruijn64 >> 58 // extract part of deBruijn sequence - i := int(deBruijnIdx64[y]) // convert to bit index - z := int((x - 1) >> 57 & 64) // adjustment if zero - return i + z -} - -const TotalGCFactor = 0.0004 -const SpanGCFactor = 0 - -// oneBitCount is indexed by byte and produces the -// number of 1 bits in that byte. For example 128 has 1 bit set -// and oneBitCount[128] will holds 1. -var oneBitCount = [256]uint8{ - 0, 1, 1, 2, 1, 2, 2, 3, - 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, - 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, - 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, - 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, - 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, - 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, - 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, - 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, - 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, - 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, - 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, - 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, - 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, - 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, - 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, - 5, 6, 6, 7, 6, 7, 7, 8} - -//mSpanList 支持并发的操作 -type mSpanList struct { - first *xSpan // first span in list, or nil if none - //last *xSpan // last span in list, or nil if none - lock sync.Mutex -} - -// 头插法(first) -func (list *mSpanList) insert(span *xSpan) { - if span == nil { - return - } - span.next = nil - list.lock.Lock() - defer list.lock.Unlock() - for { - addr := (*unsafe.Pointer)(unsafe.Pointer(&list.first)) - first := atomic.LoadPointer(addr) - if first == nil && atomic.CompareAndSwapPointer(addr, nil, unsafe.Pointer(span)) { - return - } - //先将新插入的赋值first,这时候会断裂为两个链。然后再赋值。 - if first != nil && atomic.CompareAndSwapPointer(addr, first, unsafe.Pointer(span)) { - span.next = (*xSpan)(first) - return - } - } -} - -func (list *mSpanList) moveHead() *xSpan { - list.lock.Lock() - defer list.lock.Unlock() - addr := (*unsafe.Pointer)(unsafe.Pointer(&list.first)) - for { - if list.first != nil { - head, next := list.first, list.first.next - if atomic.CompareAndSwapPointer(addr, unsafe.Pointer(head), unsafe.Pointer(next)) { - return head - } - } else { - return nil - } - } -} - -func (list *mSpanList) move(span *xSpan) { - list.lock.Lock() - defer list.lock.Unlock() - for { - var pre, next *xSpan - for node := list.first; node != nil; node = node.next { - if node == span { - next = span.next - break - } - pre = node - } - //并发cas - var addr *unsafe.Pointer - if pre != nil { - addr = (*unsafe.Pointer)(unsafe.Pointer(&pre.next)) - } else { - addr = (*unsafe.Pointer)(unsafe.Pointer(&list.first)) - } - if atomic.CompareAndSwapPointer(addr, unsafe.Pointer(span), unsafe.Pointer(next)) { - return - } - } -} - -func (list *mSpanList) foreach(consumer func(span *xSpan)) { - for node := list.first; node != nil; node = node.next { - consumer(node) - } -} diff --git a/src/common_test.go b/src/common_test.go deleted file mode 100644 index 75686fe..0000000 --- a/src/common_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "sync" - "testing" -) - -func TestSpanList(t *testing.T) { - waitCnt, loopCnt := 50, 50000 - list := mSpanList{} - var wait sync.WaitGroup - wait.Add(waitCnt) - for i := 0; i < waitCnt; i++ { - go func() { - defer wait.Done() - for j := 0; j < loopCnt; j++ { - list.insert(&xSpan{classIndex: uint(j)}) - } - }() - } - wait.Wait() - cnt := 0 - cntMap := make(map[uint]int) - list.foreach(func(span *xSpan) { - cnt++ - cntMap[span.classIndex] = cntMap[span.classIndex] + 1 - }) - for u, i := range cntMap { - if i != waitCnt { - t.Fatal(u, "的数目不对 cnt:", i) - } - //fmt.Println(u, i) - } - if cnt != waitCnt*loopCnt { - t.Fatal("总数不对") - } - fmt.Println(cnt) - -} diff --git a/src/entry.go b/src/entry.go deleted file mode 100644 index 193f570..0000000 --- a/src/entry.go +++ /dev/null @@ -1,808 +0,0 @@ -/* -Copyright 2014 Gavin Bong. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied. See the License for the specific -language governing permissions and limitations under the -License. -*/ - -// Package redblacktree provides a pure Golang implementation -// of a red-black tree as described by Thomas H. Cormen's et al. -// in their seminal Algorithms book (3rd ed). This data structure -// is not multi-goroutine safe. -package xmm - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "reflect" - "strings" - "sync" -) - -type NodeEntry struct { - Key []byte - Value []byte - Hash uint64 - Next *NodeEntry - - right *NodeEntry - left *NodeEntry - parent *NodeEntry - color Color //比二叉查找树要多出一个颜色属性 -} - - -type encodedNodeEntry struct { - TotalLen uint64 - KeyLen uint64 - ValueLen uint64 - Hash uint64 - Next *NodeEntry - - right *NodeEntry - left *NodeEntry - parent *NodeEntry - color Color //比二叉查找树要多出一个颜色属性 - - //追加string key、value 要不然就内存泄露了 - -} - -/*// total'len(64) + key'len(64) + key'content + -func (n *NodeEntry) encode() []byte { - var keyLen, valLen = round(uint64(len(n.Key)), 8), round(uint64(len(n.Value)), 8) - total := keyLen + valLen + 8*8 - encoded := encodedNodeEntry{ - TotalLen: total, - KeyLen: keyLen, - ValueLen: valLen, - Hash: 0, - Next: nil, - right: nil, - left: nil, - parent: nil, - color: false, - } - -}*/ - -var BytesAscSort Comparator = func(o1, o2 interface{}) int { - key1, key2 := o1.([]byte), o2.([]byte) - return bytes.Compare(key1, key2) -} - -// Color of a redblack tree node is either -// `Black` (true) & `Red` (false) -type Color bool - -// Direction points to either the Left or Right subtree -type Direction byte - -func (c Color) String() string { - switch c { - case true: - return "Black" - default: - return "Red" - } -} - -func (d Direction) String() string { - switch d { - case LEFT: - return "left" - case RIGHT: - return "right" - case NODIR: - return "center" - default: - return "not recognized" - } -} - -const ( - BLACK, RED Color = true, false -) -const ( - LEFT Direction = iota - RIGHT - NODIR -) - -// A node needs to be able to answer the query: -// (i) Who is my parent node ? -// (ii) Who is my grandparent node ? -// The zero value for Node has color Red. - -func (n *NodeEntry) String() string { - if n == nil { - return "" - } - return fmt.Sprintf("(%#v : %s)", n.Key, n.Color()) -} - -func (n *NodeEntry) Parent() *NodeEntry { - return n.parent -} - -func (n *NodeEntry) SetColor(color Color) { - n.color = color -} - -func (n *NodeEntry) Color() Color { - return n.color -} - -func (n *NodeEntry) Left() *NodeEntry { - return n.left -} - -func (n *NodeEntry) Right() *NodeEntry { - return n.right -} - -type Visitor interface { - Visit(*NodeEntry) -} - -// A redblack tree is `Visitable` by a `Visitor`. -type Visitable interface { - Walk(Visitor) -} - -// Keys must be comparable. It's mandatory to provide a Comparator, -// which returns zero if o1 == o2, -1 if o1 < o2, 1 if o1 > o2 -type Comparator func(o1, o2 interface{}) int - -// Default comparator expects keys to be of type `int`. -// Warning: if either one of `o1` or `o2` cannot be asserted to `int`, it panics. -func IntComparator(o1, o2 interface{}) int { - i1 := o1.(int) - i2 := o2.(int) - switch { - case i1 > i2: - return 1 - case i1 < i2: - return -1 - default: - return 0 - } -} - -// Keys of type `string`. -// Warning: if either one of `o1` or `o2` cannot be asserted to `string`, it panics. -func StringComparator(o1, o2 interface{}) int { - s1 := o1.(string) - s2 := o2.(string) - return bytes.Compare([]byte(s1), []byte(s2)) -} - -// Tree encapsulates the data structure. -type Tree struct { - root *NodeEntry // tip of the tree - cmp Comparator // required function to order keys - lock sync.RWMutex -} - -// `lock` protects `logger` -var loggerlock sync.Mutex -var logger *log.Logger - -func init() { - logger = log.New(ioutil.Discard, "", log.LstdFlags) -} - -// TraceOn turns on logging output to Stderr -func TraceOn() { - SetOutput(os.Stderr) -} - -// TraceOff turns off logging. -// By default logging is turned off. -func TraceOff() { - SetOutput(ioutil.Discard) -} - -// SetOutput redirects log output -func SetOutput(w io.Writer) { - loggerlock.Lock() - defer loggerlock.Unlock() - logger = log.New(w, "", log.LstdFlags) -} - -// NewTree returns an empty Tree with default comparator `IntComparator`. -// `IntComparator` expects keys to be type-assertable to `int`. -func NewTree() *Tree { - return &Tree{root: nil, cmp: IntComparator} -} - -// NewTreeWith returns an empty Tree with a supplied `Comparator`. -func NewTreeWith(c Comparator) *Tree { - return &Tree{root: nil, cmp: c} -} - -func (t *Tree) SetComparator(c Comparator) { - t.cmp = c -} - -// Get looks for the node with supplied key and returns its mapped payload. -// Return value in 1st position indicates whether any payload was found. -func (t *Tree) Get(key []byte) (bool, []byte) { - if err := mustBeValidKey(key); err != nil { - //logger.Printf("Get was prematurely aborted: %s\n", err.Error()) - return false, nil - } - ok, node := t.getNode(key) - if ok { - return true, node.Value - } else { - return false, nil - } -} - -func (t *Tree) getNode(key interface{}) (bool, *NodeEntry) { - found, parent, dir := t.GetParent(key) - if found { - if parent == nil { - return true, t.root - } else { - var node *NodeEntry - switch dir { - case LEFT: - node = parent.left - case RIGHT: - node = parent.right - } - - if node != nil { - return true, node - } - } - } - return false, nil -} - -// getMinimum returns the node with minimum key starting -// at the subtree rooted at node x. Assume x is not nil. -func (t *Tree) getMinimum(x *NodeEntry) *NodeEntry { - for { - if x.left != nil { - x = x.left - } else { - return x - } - } -} - -// GetParent looks for the node with supplied key and returns the parent node. -func (t *Tree) GetParent(key interface{}) (found bool, parent *NodeEntry, dir Direction) { - if err := mustBeValidKey(key); err != nil { - //logger.Printf("GetParent was prematurely aborted: %s\n", err.Error()) - return false, nil, NODIR - } - - if t.root == nil { - return false, nil, NODIR - } - t.lock.RLock() - defer t.lock.RUnlock() - return t.internalLookup(nil, t.root, key, NODIR) -} - -func (t *Tree) internalLookup(parent *NodeEntry, this *NodeEntry, key interface{}, dir Direction) (bool, *NodeEntry, Direction) { - switch { - case this == nil: - return false, parent, dir - case t.cmp(key, this.Key) == 0: - return true, parent, dir - case t.cmp(key, this.Key) < 0: - return t.internalLookup(this, this.left, key, LEFT) - case t.cmp(key, this.Key) > 0: - return t.internalLookup(this, this.right, key, RIGHT) - default: - return false, parent, NODIR - } -} - -// Reverses actions of RotateLeft -func (t *Tree) RotateRight(y *NodeEntry) { - if y == nil { - //logger.Printf("RotateRight: nil arg cannot be rotated. Noop\n") - return - } - if y.left == nil { - //logger.Printf("RotateRight: y has nil left subtree. Noop\n") - return - } - //logger.Printf("\t\t\trotate right of %s\n", y) - x := y.left - y.left = x.right - if x.right != nil { - x.right.parent = y - } - x.parent = y.parent - if y.parent == nil { - t.root = x - } else { - if y == y.parent.left { - y.parent.left = x - } else { - y.parent.right = x - } - } - x.right = y - y.parent = x -} - -// Side-effect: red-black tree properties is maintained. -func (t *Tree) RotateLeft(x *NodeEntry) { - if x == nil { - //logger.Printf("RotateLeft: nil arg cannot be rotated. Noop\n") - return - } - if x.right == nil { - //logger.Printf("RotateLeft: x has nil right subtree. Noop\n") - return - } - //logger.Printf("\t\t\trotate left of %s\n", x) - - y := x.right - x.right = y.left - if y.left != nil { - y.left.parent = x - } - y.parent = x.parent - if x.parent == nil { - t.root = y - } else { - if x == x.parent.left { - x.parent.left = y - } else { - x.parent.right = y - } - } - y.left = x - x.parent = y -} - -// Put saves the mapping (key, data) into the tree. -// If a mapping identified by `key` already exists, it is overwritten. -// Constraint: Not everything can be a key. -func (t *Tree) Put(node *NodeEntry) error { - node.parent, node.left, node.Next, node.right = nil, nil, nil, nil - key, data := node.Key, node.Value - if err := mustBeValidKey(key); err != nil { - //logger.Printf("Put was prematurely aborted: %s\n", err.Error()) - return err - } - t.lock.Lock() - defer t.lock.Unlock() - if t.root == nil { - node.color = BLACK - t.root = node - //logger.Printf("Added %s as root node\n", t.root.String()) - return nil - } - - found, parent, dir := t.internalLookup(nil, t.root, key, NODIR) - if found { - if parent == nil { - //logger.Printf("Put: parent=nil & found. Overwrite ROOT node\n") - t.root.Value = data - } else { - //logger.Printf("Put: parent!=nil & found. Overwriting\n") - switch dir { - case LEFT: - parent.left.Value = data - case RIGHT: - parent.right.Value = data - } - } - - } else { - if parent != nil { - node.parent = parent - newNode := node - switch dir { - case LEFT: - parent.left = newNode - case RIGHT: - parent.right = newNode - } - //logger.Printf("Added %s to %s node of parent %s\n", newNode.String(), dir, parent.String()) - t.fixupPut(newNode) - } - } - return nil -} - -func isRed(n *NodeEntry) bool { - key := reflect.ValueOf(n) - if key.IsNil() { - return false - } else { - return n.color == RED - } -} - -// fix possible violations of red-black-tree properties -// with combinations of: -// 1. recoloring -// 2. rotations -// -// Preconditions: -// P1) z is not nil -// -// @param z - the newly added Node to the tree. -func (t *Tree) fixupPut(z *NodeEntry) { - //logger.Printf("\tfixup new node z %s\n", z.String()) -loop: - for { - //logger.Printf("\tcurrent z %s\n", z.String()) - switch { - case z.parent == nil: - fallthrough - case z.parent.color == BLACK: - fallthrough - default: - // When the loop terminates, it does so because p[z] is black. - //logger.Printf("\t\t=> bye\n") - break loop - case z.parent.color == RED: - grandparent := z.parent.parent - //logger.Printf("\t\tgrandparent is nil %t addr:%d\n", grandparent == nil, unsafe.Pointer(t)) - if z.parent == grandparent.left { - //logger.Printf("\t\t%s is the left child of %s\n", z.parent, grandparent) - y := grandparent.right - ////logger.Printf("\t\ty (right) %s\n", y) - if isRed(y) { - // case 1 - y is RED - //logger.Printf("\t\t(*) case 1\n") - z.parent.color = BLACK - y.color = BLACK - grandparent.color = RED - z = grandparent - - } else { - if z == z.parent.right { - // case 2 - //logger.Printf("\t\t(*) case 2\n") - z = z.parent - t.RotateLeft(z) - } - - // case 3 - //logger.Printf("\t\t(*) case 3\n") - z.parent.color = BLACK - grandparent.color = RED - t.RotateRight(grandparent) - } - } else { - //logger.Printf("\t\t%s is the right child of %s\n", z.parent, grandparent) - y := grandparent.left - //logger.Printf("\t\ty (left) %s\n", y) - if isRed(y) { - // case 1 - y is RED - //logger.Printf("\t\t..(*) case 1\n") - z.parent.color = BLACK - y.color = BLACK - grandparent.color = RED - z = grandparent - - } else { - //logger.Printf("\t\t## %s\n", z.parent.left) - if z == z.parent.left { - // case 2 - //logger.Printf("\t\t..(*) case 2\n") - z = z.parent - t.RotateRight(z) - } - - // case 3 - //logger.Printf("\t\t..(*) case 3\n") - z.parent.color = BLACK - grandparent.color = RED - t.RotateLeft(grandparent) - } - } - } - } - t.root.color = BLACK -} - -// Size returns the number of items in the tree. -func (t *Tree) Size() uint64 { - visitor := &countingVisitor{} - t.Walk(visitor) - return visitor.Count -} - -// Has checks for existence of a item identified by supplied key. -func (t *Tree) Has(key interface{}) bool { - if err := mustBeValidKey(key); err != nil { - //logger.Printf("Has was prematurely aborted: %s\n", err.Error()) - return false - } - found, _, _ := t.internalLookup(nil, t.root, key, NODIR) - return found -} - -func (t *Tree) transplant(u *NodeEntry, v *NodeEntry) { - if u.parent == nil { - t.root = v - } else if u == u.parent.left { - u.parent.left = v - } else { - u.parent.right = v - } - if v != nil && u != nil { - v.parent = u.parent - } -} - -// Delete removes the item identified by the supplied key. -// Delete is a noop if the supplied key doesn't exist. -func (t *Tree) Delete(key []byte) *NodeEntry { - if !t.Has(key) { - //logger.Printf("Delete: bail as no node exists for key %d\n", key) - return nil - } - _, z := t.getNode(key) - y := z - yOriginalColor := y.color - var x *NodeEntry - - if z.left == nil { - // one child (RIGHT) - //logger.Printf("\t\tDelete: case (a)\n") - x = z.right - //logger.Printf("\t\t\t--- x is right of z") - t.transplant(z, z.right) - - } else if z.right == nil { - // one child (LEFT) - //logger.Printf("\t\tDelete: case (b)\n") - x = z.left - //logger.Printf("\t\t\t--- x is left of z") - t.transplant(z, z.left) - - } else { - // two children - //logger.Printf("\t\tDelete: case (c) & (d)\n") - y = t.getMinimum(z.right) - //logger.Printf("\t\t\tminimum of z.right is %s (color=%s)\n", y, y.color) - yOriginalColor = y.color - x = y.right - //logger.Printf("\t\t\t--- x is right of minimum") - - if y.parent == z { - if x != nil { - x.parent = y - } - } else { - t.transplant(y, y.right) - y.right = z.right - y.right.parent = y - } - t.transplant(z, y) - y.left = z.left - y.left.parent = y - y.color = z.color - } - if yOriginalColor == BLACK { - t.fixupDelete(x) - } - return z -} - -func (t *Tree) fixupDelete(x *NodeEntry) { - //logger.Printf("\t\t\tfixupDelete of node %s\n", x) - if x == nil { - return - } -loop: - for { - switch { - case x == t.root: - //logger.Printf("\t\t\t=> bye .. is root\n") - break loop - case x.color == RED: - //logger.Printf("\t\t\t=> bye .. RED\n") - break loop - case x == x.parent.right: - //logger.Printf("\t\tBRANCH: x is right child of parent\n") - w := x.parent.left // is nillable - if isRed(w) { - // Convert case 1 into case 2, 3, or 4 - //logger.Printf("\t\t\tR> case 1\n") - w.color = BLACK - x.parent.color = RED - t.RotateRight(x.parent) - w = x.parent.left - } - if w != nil { - switch { - case !isRed(w.left) && !isRed(w.right): - // case 2 - both children of w are BLACK - //logger.Printf("\t\t\tR> case 2\n") - w.color = RED - x = x.parent // recurse up tree - case isRed(w.right) && !isRed(w.left): - // case 3 - right child RED & left child BLACK - // convert to case 4 - //logger.Printf("\t\t\tR> case 3\n") - w.right.color = BLACK - w.color = RED - t.RotateLeft(w) - w = x.parent.left - } - if isRed(w.left) { - // case 4 - left child is RED - //logger.Printf("\t\t\tR> case 4\n") - w.color = x.parent.color - x.parent.color = BLACK - w.left.color = BLACK - t.RotateRight(x.parent) - x = t.root - } - } - case x == x.parent.left: - //logger.Printf("\t\tBRANCH: x is left child of parent\n") - w := x.parent.right // is nillable - if isRed(w) { - // Convert case 1 into case 2, 3, or 4 - //logger.Printf("\t\t\tL> case 1\n") - w.color = BLACK - x.parent.color = RED - t.RotateLeft(x.parent) - w = x.parent.right - } - if w != nil { - switch { - case !isRed(w.left) && !isRed(w.right): - // case 2 - both children of w are BLACK - //logger.Printf("\t\t\tL> case 2\n") - w.color = RED - x = x.parent // recurse up tree - case isRed(w.left) && !isRed(w.right): - // case 3 - left child RED & right child BLACK - // convert to case 4 - //logger.Printf("\t\t\tL> case 3\n") - w.left.color = BLACK - w.color = RED - t.RotateRight(w) - w = x.parent.right - } - if isRed(w.right) { - // case 4 - right child is RED - //logger.Printf("\t\t\tL> case 4\n") - w.color = x.parent.color - x.parent.color = BLACK - w.right.color = BLACK - t.RotateLeft(x.parent) - x = t.root - } - } - } - } - x.color = BLACK -} - -// Walk accepts a Visitor -func (t *Tree) Walk(visitor Visitor) { - visitor.Visit(t.root) -} - -func (t *Tree) GetRoot() *NodeEntry { - return t.root -} - -// countingVisitor counts the number -// of nodes in the tree. -type countingVisitor struct { - Count uint64 -} - -func (v *countingVisitor) Visit(node *NodeEntry) { - if node == nil { - return - } - - v.Visit(node.left) - v.Count = v.Count + 1 - v.Visit(node.right) -} - -// InorderVisitor walks the tree in inorder fashion. -// This visitor maintains internal state; thus do not -// reuse after the completion of a walk. -type InorderVisitor struct { - buffer bytes.Buffer -} - -func (v *InorderVisitor) Eq(other *InorderVisitor) bool { - if other == nil { - return false - } - return v.String() == other.String() -} - -func (v *InorderVisitor) trim(s string) string { - return strings.TrimRight(strings.TrimRight(s, "ed"), "lack") -} - -func (v *InorderVisitor) String() string { - return v.buffer.String() -} - -func (v *InorderVisitor) Visit(node *NodeEntry) { - if node == nil { - v.buffer.Write([]byte(".")) - return - } - v.buffer.Write([]byte("(")) - v.Visit(node.left) - v.buffer.Write([]byte(fmt.Sprintf("%s", node.Key))) // @TODO - //v.buffer.Write([]byte(fmt.Sprintf("%d{%s}", node.Key, v.trim(node.color.String())))) - v.Visit(node.right) - v.buffer.Write([]byte(")")) -} - -type HookVisitor struct { - Hook func(node *NodeEntry) -} - -func (v *HookVisitor) Visit(node *NodeEntry) { - if node == nil { - return - } - v.Hook(node) - v.Visit(node.left) - v.Visit(node.right) -} - -var ( - ErrorKeyIsNil = errors.New("The literal nil not allowed as keys") - ErrorKeyDisallowed = errors.New("Disallowed key typsssssssse") -) - -// Allowed key types are: Boolean, Integer, Floating point, Complex, String values -// And structs containing these. -// @TODO Should pointer type be allowed ? -func mustBeValidKey(key interface{}) error { - if key == nil { - return ErrorKeyIsNil - } - - /*keyValue := reflect.ValueOf(key) - switch keyValue.Kind() { - case reflect.Chan: - fallthrough - case reflect.Func: - fallthrough - case reflect.Interface: - fallthrough - case reflect.Map: - fallthrough - case reflect.Ptr: - return ErrorKeyDisallowed - default: - return nil - }*/ - return nil -} diff --git a/src/heap.go b/src/heap.go deleted file mode 100644 index d84c08b..0000000 --- a/src/heap.go +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "fmt" - "log" - "reflect" - "sync" - "sync/atomic" - "time" - "unsafe" -) - -type xHeap struct { - lock sync.Mutex - - freeChunks *xTreap // treap树 - - rawLinearMemoryAlloc linearAlloc - - //默认RawMemoryL1Bits=0,退化为以为数据。RawMemoryL2Bits = 20个 - addrMap [1 << RawMemoryL1Bits]*[1 << RawMemoryL2Bits]*xRawLinearMemory //addr -> page -> rawMemory 关系 - - allChunk []*xChunk - - allChunkAllocator *xSliceAllocator - - chunkAllocator *xAllocator - - spanAllocator *xAllocator - - rawLinearMemoryAllocator *xAllocator - - classSpan [_NumSizeClasses]*xClassSpan - - totalCapacity int64 - - freeCapacity int64 - - sweepIndex uint32 - - sweepCtl int32 // -10000<= sweepCtl < 0 正在扩容 0:非扩容状态 - - sweepLastTime time.Time -} - -const sweepCtlStatus = -68 - -func newXHeap() (*xHeap, error) { - call := func(inuse uintptr) { log.Printf("XSliceAllocator xChunk 扩容了,使用了 inuse:%d\n", inuse) } - chunkAllocator := newXAllocator(unsafe.Sizeof(xChunk{})) - valAllocator := newXAllocator(unsafe.Sizeof(treapNode{})) - spanAllocator := newXAllocator(unsafe.Sizeof(xSpan{})) - allChunkAllocator, err := newXSliceAllocator(unsafe.Sizeof(&xChunk{}), 16, call) - rawLinearMemoryAllocator := newXAllocator(unsafe.Sizeof(xRawLinearMemory{})) - if err != nil { - return nil, err - } - freeChunks := newXTreap(valAllocator) - heap := &xHeap{allChunkAllocator: allChunkAllocator, chunkAllocator: chunkAllocator, freeChunks: freeChunks, - spanAllocator: spanAllocator, rawLinearMemoryAllocator: rawLinearMemoryAllocator} - if err := heap.rawLinearMemoryAlloc.expand(nil, heapRawMemoryBytes); err != nil { - return nil, err - } - if err := heap.initClassSpan(); err != nil { - return nil, err - } - return heap, nil -} - -func (xh *xHeap) initClassSpan() error { - for i := 0; i < _NumSizeClasses; i++ { - classSpan := &xClassSpan{} - xh.classSpan[i] = classSpan - if err := classSpan.Init(uint(i), xh); err != nil { - return err - } - } - return nil -} - -func (xh *xHeap) addFreeCapacity(size int64) { - for { - val := atomic.LoadInt64(&xh.freeCapacity) - newVal := val + size - if atomic.CompareAndSwapInt64(&xh.freeCapacity, val, newVal) { - return - } - } -} - -func (xh *xHeap) addChunks(xChunks []*xChunk) error { - if xh.allChunk == nil || len(xChunks)+len(xh.allChunk) > cap(xh.allChunk) { - cap := cap(xh.allChunk) << 1 - //log.Printf("xHeap.addChunks grow cap:%d\n", cap) - err := xh.allChunkAllocator.grow(uintptr(cap), func(newPtr unsafe.Pointer) error { - sl := reflect.SliceHeader{ - Data: uintptr(newPtr), - Len: len(xh.allChunk), - Cap: cap, - } - newXChunks := *(*[]*xChunk)(unsafe.Pointer(&sl)) - length := copy(newXChunks, xh.allChunk[:len(xh.allChunk)]) - if length != len(xh.allChunk) { - return errors.New("copy 数量错误") - } - xh.allChunk = newXChunks - return nil - }) - if err != nil { - return err - } - } - xh.allChunk = append(xh.allChunk, xChunks...) - return nil -} - -func (xh *xHeap) allocRawSpan(pageNum uintptr) (span *xSpan, err error) { - chunkP, err := xh.spanAllocator.alloc() - if err != nil { - return nil, err - } - // 增加chunks、free、addrMap - span = (*xSpan)(chunkP) - chunk, err := xh.allocChunk(pageNum) - if err != nil { - return nil, err - } - xh.setSpans(chunk.startAddr, pageNum, span) - // chunk -> xSpan - span.freeIndex = 0 - span.npages = pageNum - span.startAddr = chunk.startAddr - return span, nil -} - -func (xh *xHeap) setSpans(base, npage uintptr, s *xSpan) { - p := base / _PageSize - ai := RawMemoryIndex(base) - ha := xh.addrMap[ai.l1()][ai.l2()] - for n := uintptr(0); n < npage; n++ { - i := (p + n) % pagesPerRawMemory - if i == 0 { - ai = RawMemoryIndex(base + n*_PageSize) - ha = xh.addrMap[ai.l1()][ai.l2()] - } - ha.spans[i] = s - } -} - -func (xh *xHeap) spanOf(p uintptr) (*xSpan, error) { - ri := RawMemoryIndex(p) - if RawMemoryL1Bits == 0 { - if ri.l2() >= uint(len(xh.addrMap[0])) { - return nil, fmt.Errorf("err: l2(%d)is err", ri.l2()) - } - } else { - if ri.l1() >= uint(len(xh.addrMap)) { - return nil, fmt.Errorf("err: l1(%d)is err", ri.l1()) - } - } - l2 := xh.addrMap[ri.l1()] - if RawMemoryL1Bits != 0 && l2 == nil { // Should never happen if there's no L1. - return nil, fmt.Errorf("err: l1(%d)is nil", ri.l2()) - } - ha := l2[ri.l2()] - if ha == nil { - return nil, fmt.Errorf("err: l2(%d)is nil", ri.l2()) - } - return ha.spans[(p/_PageSize)%pagesPerRawMemory], nil -} - -func (xh *xHeap) allocSpan(pageNum uintptr, index uint, class uintptr, fact float32) (span *xSpan, err error) { - chunkP, err := xh.spanAllocator.alloc() - if err != nil { - return nil, err - } - // 增加chunks、free、addrMap - span = (*xSpan)(chunkP) - if pageNum > 0 { - chunk, err := xh.allocChunk(pageNum) - if err != nil { - return nil, err - } - xh.setSpans(chunk.startAddr, pageNum, span) - span.startAddr = chunk.startAddr - } - span.classIndex = index - span.classSize = class - span.freeIndex = 0 - span.npages = pageNum - if err := span.Init(fact, xh); err != nil { - return nil, err - } - return span, nil -} - -func (xh *xHeap) allocChunk(pageNum uintptr) (ptr *xChunk, err error) { - chunk, err := xh.freeChunk(pageNum) - if err != nil { - return nil, err - } - return chunk, err -} - -// 必须加锁分配 -func (xh *xHeap) freeChunk(pageNum uintptr) (ptr *xChunk, err error) { - xh.lock.Lock() - defer xh.lock.Unlock() - node, err := xh.freeChunks.find(pageNum) - if err == EmptyError { - if err := xh.grow2(pageNum); err != nil { - return nil, err - } - node, err = xh.freeChunks.find(pageNum) - } - if err != nil { - return nil, err - } - if node.chunk == nil { - return nil, errors.New("node val is nil") - } - if node.npagesKey < pageNum { - return nil, errors.New("node val is small") - } - startAddr, npages := node.chunk.startAddr, node.chunk.npages - if err := xh.freeChunks.removeNode(node); err != nil { - return nil, err - } - if node.npagesKey == pageNum { - return &xChunk{startAddr: startAddr, npages: pageNum}, nil - } - node.chunk.npages = npages - pageNum - node.chunk.startAddr = startAddr + pageNum*_PageSize - if err := xh.freeChunks.insert(node.chunk); err != nil { - return nil, err - } - return &xChunk{startAddr: startAddr, npages: pageNum}, nil -} - -//todo 释放:地址中保存len、保存空闲地址、要么直接复用,要么合并page再复用 -func (xh *xHeap) free(addr uintptr) error { - //todo 标记完成,接下来触发清理 - //panic("todo 释放:地址中保存len、保存空闲地址、要么直接复用,要么合并page再复用,存放到树状结构") - //todo 还给 span,比较高效。 - //key:开始地址 value:结束地址 存放到红黑树中。 - //key找key最相近的,找到判断value。有则更新,没有则插入。 - if err := xh.mark(addr); err != nil { - return err - } - // 统计 - xh.sweep() - return nil -} - -func (xh *xHeap) needSweep() bool { - val, sweepThreshold := atomic.LoadInt64(&xh.freeCapacity), float64(xh.totalCapacity)*TotalGCFactor - if sweepThreshold > float64(val) { - return false - } - if time.Now().Sub(xh.sweepLastTime).Seconds() <= 1 { - return false - } - sweepCtl := atomic.LoadInt32(&xh.sweepCtl) - if sweepCtl < 0 { - //扩容线程+1 - if atomic.CompareAndSwapInt32(&xh.sweepCtl, sweepCtl, sweepCtl+1) { - return true - } - } - if sweepCtl > 0 { - return false - } - if atomic.CompareAndSwapInt32(&xh.sweepCtl, sweepCtl, sweepCtlStatus) { - return true - } - return false -} - -var logg bool - -// todo classSpan中并发支持 -func (xh *xHeap) sweep() { - //统计判断 - if !xh.needSweep() { - return - } - var sweepIndex uint32 - var total uint - for sweepIndex = atomic.LoadUint32(&xh.sweepIndex); sweepIndex < _NumSizeClasses; sweepIndex = atomic.LoadUint32(&xh.sweepIndex) { - if !atomic.CompareAndSwapUint32(&xh.sweepIndex, sweepIndex, sweepIndex+1) { - continue - } - classSpan := xh.classSpan[sweepIndex] - //todo 环循环 - for span := classSpan.full.first; span != nil; span = span.next { - if logg { - fmt.Println("sweep", sweepIndex, uintptr(unsafe.Pointer(span))) - } - if sweep, size, err := xh.sweepFullSpan(span); err != nil { - log.Printf("xHeap.sweep err:%s\n ", err) - continue - } else if sweep { - total += size - classSpan.full.move(span) - } - } - } - fmt.Println("--------sweep---------", time.Now().String(), total) - xh.sweepLastTime = time.Now() - if total < 1 { - return - } - //logg = true - xh.addFreeCapacity(0 - int64(total)) - for { - sweepCtl := atomic.LoadInt32(&xh.sweepCtl) - if sweepCtl >= 0 { - return - } - if atomic.CompareAndSwapInt32(&xh.sweepCtl, sweepCtl, sweepCtl-1) { - if sweepCtl != sweepCtlStatus { - return - } - //sweep执行结束 - atomic.StoreInt32(&xh.sweepCtl, 0) - atomic.StoreUint32(&xh.sweepIndex, 0) - return - } - } -} -func (xh *xHeap) ChunkInsert(chunk *xChunk) error { - xh.lock.Lock() - defer xh.lock.Unlock() - return xh.freeChunks.insert(chunk) -} - -// 清理span(span级别锁) -func (xh *xHeap) sweepFullSpan(span *xSpan) (sweep bool, size uint, err error) { - //fmt.Println("======================") - if span.classIndex > 0 { - // 所有还给classspan - return xh.classSpan[span.classIndex].freeSpan(span) - } else if span.classIndex == 0 && span.nelems > 0 { - //大对象释放 - span.lock.Lock() - defer span.lock.Unlock() - if span.nelems != span.countGcMarkBits() { - return - } - chunkP, err := xh.chunkAllocator.alloc() - if err != nil { - return false, 0, err - } - // 增加chunks、free、addrMap - chunk := (*xChunk)(chunkP) - if TestBbulks == span.startAddr { - fmt.Println("ssssssss") - } - chunk.startAddr = span.startAddr - chunk.npages = span.npages - if err := xh.ChunkInsert(chunk); err != nil { - return false, 0, err - } - } - return true, uint(span.npages * _PageSize), nil -} - -func (xh *xHeap) mark(addr uintptr) error { - err := markBitsForAddr(addr, xh) - if err != nil { - return err - } - return nil -} - -func (xh *xHeap) grow2(pageNum uintptr) error { - size := pageNum * _PageSize - if size < heapRawMemoryBytes { - size = heapRawMemoryBytes - } - p, err := xh.rawLinearMemoryAlloc.alloc(size, heapRawMemoryBytes) - xh.totalCapacity += int64(size) - if err != nil && err != LackOfMemoryErr { - return err - } - if err == LackOfMemoryErr { - if err := xh.rawLinearMemoryAlloc.expand(nil, heapRawMemoryBytes); err != nil { - var la linearAlloc - la.expand(nil, heapRawMemoryBytes) - xh.rawLinearMemoryAlloc = la - } - return xh.grow2(pageNum) - } - chunkP, err := xh.chunkAllocator.alloc() - if err != nil { - return err - } - // 增加chunks、free、addrMap - chunk := (*xChunk)(chunkP) - chunk.startAddr = uintptr(p) - chunk.npages = size / _PageSize - xh.freeChunks.insert(chunk) - if err := xh.addChunks([]*xChunk{chunk}); err != nil { - return err - } - offset := uintptr(p) - for i := 0; i < int(Align(size, heapRawMemoryBytes)/heapRawMemoryBytes); i++ { - //页地址到RawMemory保存 - rawLinearMemoryPtr, err := xh.rawLinearMemoryAllocator.alloc() - if err != nil { - return err - } - //addrMap 初始化xRawLinearMemory - rlm := (*xRawLinearMemory)(rawLinearMemoryPtr) - index := RawMemoryIndex(offset) - if addrs := xh.addrMap[index.l1()]; addrs == nil { - var a [1 << RawMemoryL2Bits]*xRawLinearMemory - xh.addrMap[index.l1()] = &a - addrs = &a - } - xh.addrMap[index.l1()][index.l2()] = rlm - offset += heapRawMemoryBytes - } - return nil -} - -func (xh *xHeap) grow() error { - p, err := xh.rawLinearMemoryAlloc.alloc(heapRawMemoryBytes, heapRawMemoryBytes) - xh.totalCapacity += heapRawMemoryBytes - if err != nil && err != LackOfMemoryErr { - return err - } - if err == LackOfMemoryErr { - if err := xh.rawLinearMemoryAlloc.expand(nil, heapRawMemoryBytes); err != nil { - var la linearAlloc - la.expand(nil, heapRawMemoryBytes) - xh.rawLinearMemoryAlloc = la - } - return xh.grow() - } - chunkP, err := xh.chunkAllocator.alloc() - if err != nil { - return err - } - // 增加chunks、free、addrMap - chunk := (*xChunk)(chunkP) - chunk.startAddr = uintptr(p) - chunk.npages = pagesPerRawMemory - xh.freeChunks.insert(chunk) - if err := xh.addChunks([]*xChunk{chunk}); err != nil { - return err - } - //页地址到RawMemory保存 - rawLinearMemoryPtr, err := xh.rawLinearMemoryAllocator.alloc() - if err != nil { - return err - } - rlm := (*xRawLinearMemory)(rawLinearMemoryPtr) - offset := uintptr(p) - for i := 0; i < pagesPerRawMemory; i++ { - index := RawMemoryIndex(offset) - addrs := xh.addrMap[index.l1()] - if addrs == nil { - var a [1 << RawMemoryL2Bits]*xRawLinearMemory - xh.addrMap[index.l1()] = &a - addrs = &a - } - addrs[index.l2()] = rlm - } - return nil -} - -func RawMemoryIndex(p uintptr) RawMemoryIdx { - return RawMemoryIdx((p + RawMemoryBaseOffset) / heapRawMemoryBytes) -} - -// RawMemoryBase returns the low address of the region covered by heap -// RawMemory i. -func RawMemoryBase(i RawMemoryIdx) uintptr { - return uintptr(i)*heapRawMemoryBytes - RawMemoryBaseOffset -} - -type RawMemoryIdx uint - -func (i RawMemoryIdx) l1() uint { - if RawMemoryL1Bits == 0 { - // Let the compiler optimize this away if there's no - // L1 map. - return 0 - } else { - return uint(i) >> RawMemoryL1Shift - } -} - -func (i RawMemoryIdx) l2() uint { - if RawMemoryL1Bits == 0 { - return uint(i) - } else { - return uint(i) & (1< l.end { - return nil, LackOfMemoryErr - } - l.next = p + size - if pEnd := round(l.next-1, DefaultPhysPageSize); pEnd > l.mapped { - // We need to map more of the reserved space. - if err := l.sysMap(unsafe.Pointer(l.mapped), pEnd-l.mapped); err != nil { - return nil, err - } - l.mapped = pEnd - } - return unsafe.Pointer(p), nil -} - -func (l *linearAlloc) sysMap(addr unsafe.Pointer, length uintptr) error { - prot, flags, fd, offset := syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_FIXED|syscall.MAP_PRIVATE, -1, 0 - v, _, err := syscall.Syscall6(syscall.SYS_MMAP, uintptr(addr), length, uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset)) - if err == _ENOMEM || (runtime.GOOS == "solaris" && err == _sunosEAGAIN) { - return errors.New("runtime: out of memory") - } - if uintptr(addr) != v || err != 0 { - return errors.New("runtime: cannot map pages in arena address space") - } - return nil -} - -func (l *linearAlloc) errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case syscall.EAGAIN: - return errEAGAIN - case syscall.EINVAL: - return errEINVAL - case syscall.ENOENT: - return errENOENT - } - return e -} - -func (l *linearAlloc) sysReserve(addr unsafe.Pointer, size uintptr) (unsafe.Pointer, error) { - ptr, _, err := syscall.Syscall6(syscall.SYS_MMAP, uintptr(addr), size, syscall.PROT_NONE, - syscall.MAP_ANON|syscall.MAP_PRIVATE, uintptr(0), uintptr(0)) - if err != 0 { - return nil, l.errnoErr(err) - } - return unsafe.Pointer(ptr), nil -} - -// sysReserveAligned is like sysReserve, but the returned pointer is -// aligned to align bytes. It may reserve either n or n+align bytes, -// so it returns the size that was reserved. -func (l *linearAlloc) sysReserveAligned(v unsafe.Pointer, size, align uintptr) (unsafe.Pointer, uintptr, error) { - // Since the alignment is rather large in uses of this - // function, we're not likely to get it by chance, so we ask - // for a larger region and remove the parts we don't need. - retries := 0 -retry: - ptr, err := l.sysReserve(v, size+align) - if err != nil { - return nil, 0, err - } - p := uintptr(ptr) - switch { - case p == 0: - return nil, 0, nil - case p&(align-1) == 0: - // We got lucky and got an aligned region, so we can - // use the whole thing. - return unsafe.Pointer(p), size + align, nil - case runtime.GOOS == "windows": - // On Windows we can't release pieces of a - // reservation, so we release the whole thing and - // re-reserve the aligned sub-region. This may race, - // so we may have to try again. - l.sysFree(unsafe.Pointer(p), size+align) - p = round(p, align) - p2, err := l.sysReserve(unsafe.Pointer(p), size) - if err != nil { - goto retry - } - if p != uintptr(p2) { - // Must have raced. Try again. - l.sysFree(p2, size) - if retries++; retries == 100 { - return nil, 0, errors.New("failed to allocate aligned heap memory; too many retries") - } - goto retry - } - // Success. - return p2, size, nil - default: - // Trim off the unaligned parts. - pAligned := round(p, align) - l.sysFree(unsafe.Pointer(p), pAligned-p) - end := pAligned + size - endLen := (p + size + align) - end - if endLen > 0 { - l.sysFree(unsafe.Pointer(end), endLen) - } - return unsafe.Pointer(pAligned), size, nil - } -} - -func (l *linearAlloc) sysFree(addr unsafe.Pointer, length uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_MUNMAP, uintptr(addr), length, 0) - if e1 != 0 { - err = l.errnoErr(e1) - } - return -} diff --git a/src/linear_alloc_test.go b/src/linear_alloc_test.go deleted file mode 100644 index 7d32370..0000000 --- a/src/linear_alloc_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "sync" - "testing" - "time" - "unsafe" - //"xmm/src" -) - -func TestLinearAlloc(t *testing.T) { - var la linearAlloc - if er := la.init(heapRawMemoryBytes * 10); er != nil { - t.Fatal(er) - } - userSize, num := unsafe.Sizeof(User{}), 10000 - p, err := la.alloc(userSize*uintptr(num), 4096) - if err != nil { - t.Fatal(err) - } - ret := uintptr(p) - us := make([]uintptr, num) - - for n := 0; n < num; n++ { - user := (*User)(unsafe.Pointer(ret)) - user.Age = 11 - user.Name = n - //addr += uintptr(length) - us[n] = ret - ret += userSize - } - for i, u := range us { - user := (*User)(unsafe.Pointer(u)) - if user == nil || user.Age != 11 || user.Name != i { - t.Error(i, user) - } - } -} - -func TestLinearAlloc2(t *testing.T) { - var la linearAlloc - if er := la.init(heapRawMemoryBytes * 10); er != nil { - t.Fatal(er) - } - p, err := la.alloc(heapRawMemoryBytes, 4096) - if err != nil { - t.Fatal(err) - } - node := (*NodeEntry)(p) - - var wait sync.WaitGroup - wait.Add(10) - for j := 0; j < 10; j++ { - go func(z int) { - defer wait.Done() - for i := 0; i < 800000; i++ { - node.Key = []byte("keyPtr") - node.Value = []byte("valPtr") - node.Hash = 12121 - } - }(j) - } - wait.Wait() -} - -type A struct { - a byte - b int - c bool -} - -func TestLinearAlloc3(t *testing.T) { - pageSize := uintptr(4096) - var la linearAlloc - if er := la.expand(nil, heapRawMemoryBytes); er != nil { - t.Fatal(er) - } - - p, err := la.alloc(heapRawMemoryBytes, pageSize) - if err != nil { - t.Fatal(err) - } - fmt.Println(unsafe.Sizeof(NodeEntry{}), uintptr(p), round(uintptr(p), pageSize), unsafe.Offsetof(NodeEntry{}.Next)) - fmt.Println(round(unsafe.Sizeof(NodeEntry{}), 8), round(unsafe.Sizeof(A{}), 8)) - - //todo 怀疑是这个内存没有对齐 -120 cache line 0.01s -80 0.09s -20 0.22s - //todo 对齐 - ptr := uintptr(p) + pageSize*101 - 1500 - fmt.Println(ptr, ptr%8, round(ptr, pageSize), round(ptr+80, pageSize)) - node := (*NodeEntry)(unsafe.Pointer(ptr)) - var wait sync.WaitGroup - wait.Add(10) - t11 := time.Now() - for j := 0; j < 10; j++ { - go func(z int) { - defer wait.Done() - for i := 0; i < 800000; i++ { - node.Key = []byte("keyPtr") - node.Value = []byte("valPtr") - node.Hash = 12121 - } - }(j) - } - wait.Wait() - fmt.Println(time.Now().Sub(t11), uintptr(unsafe.Pointer(&node.Value))) -} - -func TestLinearCopyAlloc4(t *testing.T) { - pageSize := uintptr(4096) - var la linearAlloc - if er := la.expand(nil, heapRawMemoryBytes); er != nil { - t.Fatal(er) - } - - p, err := la.alloc(heapRawMemoryBytes, pageSize) - if err != nil { - t.Fatal(err) - } - ptr := uintptr(p) + pageSize*101 - 1500 - fmt.Println(float64(pageSize*101)*0.1/8.0, ptr) -} diff --git a/src/metadata.go b/src/metadata.go deleted file mode 100644 index 6d9bf78..0000000 --- a/src/metadata.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -const ( - _ClassSize = 256 - // 1<<12 4k大小 - _PageShift = 12 - - _FixAllocChunk = 16 << 10 // Chunk size for FixAlloc - - RawMemoryL1Bits = 0 - //堆中地址占48位 - heapAddrBits = 48 - //每个RawMemory的1<<32,占28bit - logHeapRawMemoryBytes = 28 - - //page大小 4K - _PageSize = 1 << _PageShift - - //每个RawMemory的大小256M - heapRawMemoryBytes = 1 << logHeapRawMemoryBytes - - //每个RawMemory的page数 65536 - pagesPerRawMemory = heapRawMemoryBytes / _PageSize - - // 48bit = L1Bits + L2Bits + 堆中byte偏移量 - RawMemoryL2Bits = heapAddrBits - logHeapRawMemoryBytes - RawMemoryL1Bits - - // RawMemoryL1Shift is the number of bits to shift an RawMemory frame - // number by to compute an index into the first level RawMemory map. - RawMemoryL1Shift = RawMemoryL2Bits - - // RawMemoryBits is the total bits in a combined RawMemory map index. - // This is split between the index into the L1 RawMemory map and - // the L2 RawMemory map. - RawMemoryBits = RawMemoryL1Bits + RawMemoryL2Bits - - allChunkInitSize = 1 << 10 - - RawMemoryBaseOffset uintptr = 1 * (1 << 47) -) - -const ( - _MaxSmallSize = 32768 //4K - smallSizeDiv = 8 - smallSizeMax = 1024 - largeSizeDiv = 128 - _NumSizeClasses = 67 -) - -var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768} -var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4} - -type divMagic struct { - shift uint8 - shift2 uint8 - mul uint16 - baseMask uint16 -} - -var class_to_divmagic = [_NumSizeClasses]divMagic{{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {5, 0, 1, 65504}, {4, 9, 171, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 9, 57, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 7, 43, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 57, 0}, {7, 6, 13, 0}, {6, 11, 187, 0}, {8, 5, 11, 0}, {7, 8, 37, 0}, {10, 0, 1, 64512}, {7, 9, 57, 0}, {8, 6, 13, 0}, {7, 11, 187, 0}, {9, 5, 11, 0}, {8, 8, 37, 0}, {11, 0, 1, 63488}, {8, 9, 57, 0}, {7, 10, 49, 0}, {10, 5, 11, 0}, {7, 10, 41, 0}, {7, 9, 19, 0}, {12, 0, 1, 61440}, {8, 9, 27, 0}, {8, 10, 49, 0}, {11, 5, 11, 0}, {7, 13, 161, 0}, {7, 13, 155, 0}, {8, 9, 19, 0}, {13, 0, 1, 57344}, {8, 12, 111, 0}, {9, 9, 27, 0}, {11, 6, 13, 0}, {7, 14, 193, 0}, {12, 3, 3, 0}, {8, 13, 155, 0}, {11, 8, 37, 0}, {14, 0, 1, 49152}, {11, 8, 29, 0}, {7, 13, 55, 0}, {12, 5, 7, 0}, {8, 14, 193, 0}, {13, 3, 3, 0}, {7, 14, 77, 0}, {12, 7, 19, 0}, {15, 0, 1, 32768}} -var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31} -var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{31, 32, 33, 34, 35, 36, 36, 37, 37, 38, 38, 39, 39, 39, 40, 40, 40, 41, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66} diff --git a/src/metadata_alloc.go b/src/metadata_alloc.go deleted file mode 100644 index 567e75f..0000000 --- a/src/metadata_alloc.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "sync" - "sync/atomic" - "unsafe" -) - -// persistentChunkSize is the number of bytes we allocate when we grow -// a persistentAlloc. -const metadataRawMemoryBytes = 256 << 20 - -// 申请固定大小申请 -type xFixedAllocator struct { - - //固定的 - size uintptr - - addr uintptr - - //所有mmap的内存 - free *mRawlink - - //使用的 - chunk uintptr - - //未用的 - nchunk uintptr - - //mmap的内存 - freeRawMemory *xRawMemory - - growCall func(inuse uintptr) - - inuse uintptr - - lock sync.Mutex -} - -func newXFixedAllocator(size uintptr, growCall func(inuse uintptr)) (*xFixedAllocator, error) { - if size > metadataRawMemoryBytes { - return nil, errors.New("size 超过了最大newXAllocator freeRawMemory 内存") - } - xrm, err := newXRawMemory(metadataRawMemoryBytes) - if err != nil { - return nil, err - } - return &xFixedAllocator{size: size, freeRawMemory: xrm, chunk: 0, nchunk: metadataRawMemoryBytes, growCall: growCall}, nil -} - -type mRawlink struct { - next *mRawlink -} - -func (xa *xFixedAllocator) alloc() (unsafe.Pointer, error) { - nchunk := xa.casNchunk() - if nchunk < xa.size { - if err := xa.grow(); err != nil { - return nil, err - } - } - chunk := xa.casChunk() - offset := atomic.LoadUintptr(&xa.freeRawMemory.addr) + chunk - xa.inuse += xa.size - return unsafe.Pointer(offset), nil -} - -func (xa *xFixedAllocator) grow() error { - xa.lock.Lock() - defer xa.lock.Unlock() - if xa.nchunk >= xa.size { - return nil - } - // todo 原子化 - if xa.growCall != nil { - xa.growCall(xa.inuse) - } - xrm, err := newXRawMemory(metadataRawMemoryBytes) - if err != nil { - return err - } - xa.freeRawMemory = xrm - xa.chunk = 0 - xa.nchunk = metadataRawMemoryBytes - return nil -} - -func (xa *xFixedAllocator) casNchunk() (old uintptr) { - size := xa.size //不变的size - var swapped bool - for /*retry := 3; retry > 0; retry--*/ { - oldVal := atomic.LoadUintptr(&xa.nchunk) - newVal := oldVal - size - swapped = atomic.CompareAndSwapUintptr(&xa.nchunk, oldVal, newVal) - if swapped { - return oldVal - } - } -} - -func (xa *xFixedAllocator) casChunk() (old uintptr) { - size := xa.size //不变的size - var swapped bool - for /*retry := 3; retry > 0; retry--*/ { - oldVal := atomic.LoadUintptr(&xa.chunk) - newVal := oldVal + size - swapped = atomic.CompareAndSwapUintptr(&xa.chunk, oldVal, newVal) - if swapped { - return oldVal - } - } -} - -func (xa *xFixedAllocator) Close() error { - return xa.freeRawMemory.Close() -} - -type xSliceAllocator struct { - *xFixedAllocator - initSize uintptr - lock sync.Mutex -} - -func newXSliceAllocator(elementSize uintptr, initSize uintptr, growCall func(inuse uintptr)) (*xSliceAllocator, error) { - a, err := newXFixedAllocator(elementSize, growCall) - if err != nil { - return nil, err - } - if initSize < metadataRawMemoryBytes/elementSize { - initSize = metadataRawMemoryBytes / elementSize - } - return &xSliceAllocator{initSize: initSize, xFixedAllocator: a}, nil -} - -func (xa *xSliceAllocator) batchAlloc(size, cap uintptr) (ptr unsafe.Pointer, old *xRawMemory, err error) { - xa.lock.Lock() - defer xa.lock.Unlock() - if xa.nchunk < size*xa.size { - if xa.growCall != nil { - xa.growCall(xa.inuse) - } - if cap < xa.initSize { - cap = xa.initSize << 1 - xa.initSize = cap - } - if cap < 1 { - return nil, nil, errors.New("cap == 0") - } - nc := int(cap * xa.size) - xrm, err := newXRawMemory(nc) - if err != nil { - return nil, nil, err - } - old = xa.freeRawMemory - xa.freeRawMemory = xrm - xa.chunk = 0 - xa.nchunk = uintptr(nc) - } - offset := xa.freeRawMemory.addr + xa.chunk - xa.chunk += size * xa.size - xa.nchunk -= size * xa.size - xa.inuse += size * xa.size - return unsafe.Pointer(offset), nil, nil -} - -func (xa *xSliceAllocator) alloc(cap uintptr) (ptr unsafe.Pointer, old *xRawMemory, err error) { - xa.lock.Lock() - defer xa.lock.Unlock() - if xa.nchunk < xa.size { - xa.growCall(xa.inuse) - nc := int(cap * xa.size) - xrm, err := newXRawMemory(nc) - if err != nil { - return nil, nil, err - } - old = xa.freeRawMemory - xa.freeRawMemory = xrm - xa.chunk = 0 - xa.nchunk = uintptr(nc) - } - offset := xa.freeRawMemory.addr + xa.chunk - xa.chunk += xa.size - xa.nchunk -= xa.size - xa.inuse += xa.size - return unsafe.Pointer(offset), nil, nil -} - -func (xa *xSliceAllocator) grow(newCap uintptr, copy func(new unsafe.Pointer) error) error { - if newCap < xa.initSize { - newCap = xa.initSize - } - ptr, old, err := xa.alloc(newCap) - if err != nil { - return err - } - if err := copy(ptr); err != nil { - xa.freeRawMemory.Close() - xa.freeRawMemory = old - return err - } - if old != nil { - if err := old.Close(); err != nil { - return err - } - } - return nil -} - -type xAllocator struct { - size uintptr - inuse uintptr -} - -func newXAllocator(size uintptr) *xAllocator { - return &xAllocator{size: size} -} - -func (xa *xAllocator) alloc() (unsafe.Pointer, error) { - xa.inuse += xa.size - return pool.alloc(xa.size) -} diff --git a/src/raw_linear_memory.go b/src/raw_linear_memory.go deleted file mode 100644 index 1c451fb..0000000 --- a/src/raw_linear_memory.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -type xRawLinearMemory struct { - //bitmap [heapArenaBitmapBytes]byte - spans [pagesPerRawMemory]*xSpan - pageInUse [pagesPerRawMemory / 8]uint8 - pageMarks [pagesPerRawMemory / 8]uint8 -} diff --git a/src/raw_memory.go b/src/raw_memory.go deleted file mode 100644 index 2b5d8b9..0000000 --- a/src/raw_memory.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "sync" - "sync/atomic" - "syscall" - "unsafe" -) - -//用来管理mmap申请的内存,用于实际存放地址的元数据 -type xRawMemory struct { - addr uintptr - down bool - next *xRawMemory - mem []byte -} - -func newXRawMemory(byteNum int) (*xRawMemory, error) { - - mem, err := syscall.Mmap(-1, 0, byteNum, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE) - if err != nil { - return nil, err - } - //log.Printf(" newXRawMemory(mmap) byteNum:%d byte \n", byteNum) - ptr := unsafe.Pointer(&mem[0]) - xrm := &xRawMemory{addr: uintptr(ptr), mem: mem} - return xrm, nil -} - -func (xrm *xRawMemory) Close() error { - return syscall.Munmap(xrm.mem) -} - -type block struct { - addr uintptr - len uintptr - end uintptr -} - -var pool *xRawMemoryPool - -func init() { - pool = &xRawMemoryPool{} -} - -// -type xRawMemoryPool struct { - //空闲free - frees []*block - //申请内存,头插法 - xrm *xRawMemory - index uintptr - lock sync.RWMutex -} - -// alloc 页对齐 -func (xrmp *xRawMemoryPool) alloc(byteSize uintptr) (ptr unsafe.Pointer, err error) { - xrmp.lock.Lock() - defer xrmp.lock.Unlock() - size := byteSize - offset, err := xrmp.alignOf(size) - if err != nil { - return nil, err - } - if offset == 0 { - //扩容 - xrm, err := newXRawMemory(metadataRawMemoryBytes) - if err != nil { - return nil, err - } - next := xrmp.xrm - xrm.next = next - xrmp.xrm = xrm - } - return unsafe.Pointer(xrmp.xrm.addr + offset), nil -} - -func (xrmp *xRawMemoryPool) release(block *block) error { - xrmp.lock.Lock() - defer xrmp.lock.Unlock() - xrmp.frees = append(xrmp.frees, block) - return nil -} - -func (xrmp *xRawMemoryPool) grow() error { - xrmp.lock.Lock() - defer xrmp.lock.Unlock() - xrm, err := newXRawMemory(metadataRawMemoryBytes) - if err != nil { - return err - } - next := xrmp.xrm - xrm.next = next - xrmp.xrm = xrm - return nil -} - -//通过cas获取空闲的offset -func (xrmp *xRawMemoryPool) freeOffset(size uintptr) (uintptr, error) { - if size > metadataRawMemoryBytes { - return 0, errors.New("size is over") - } - var swapped bool - for /*retry := 3; retry > 0; retry--*/ { - oldVal := atomic.LoadUintptr(&xrmp.index) - offset := oldVal % metadataRawMemoryBytes - if _PageSize-offset%_PageSize < size { - // 当前page不够,挪动到下一个page - offset = Align(offset, _PageSize) - } - index := offset + size - // 当前xRawMemory 不够,将创建一个RawMemory - if index > metadataRawMemoryBytes { - index = size - offset = 0 - } - swapped = atomic.CompareAndSwapUintptr(&xrmp.index, oldVal, index) - if swapped { - return offset, nil - } - } -} - -func (xrmp *xRawMemoryPool) alignOf(size uintptr) (uintptr, error) { - if size > metadataRawMemoryBytes { - return 0, errors.New("size is over[xRawMemoryPool]") - } - offset := xrmp.index % metadataRawMemoryBytes - if _PageSize-offset%_PageSize < size { - // 当前page不够,挪动到下一个page - offset = Align(offset, _PageSize) - } - xrmp.index = offset + size - // 当前xRawMemory 不够,将创建一个RawMemory - if xrmp.index > metadataRawMemoryBytes { - xrmp.index = size - offset = 0 - } - return offset, nil -} diff --git a/src/raw_memory_test.go b/src/raw_memory_test.go deleted file mode 100644 index 3275ec0..0000000 --- a/src/raw_memory_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "syscall" - "testing" - "time" - "unsafe" -) - -func TestSet(t *testing.T) { - var first, last unsafe.Pointer - for i := 0; i < 10000; i++ { - ptr, err := pool.alloc(_PageSize >> 3) - if err != nil { - t.Fatal(err) - } - if first == nil { - fmt.Printf("first: %d\n", ptr) - first = ptr - } - last = ptr - } - fmt.Printf("last: %d \n ", metadataRawMemoryBytes/_PageSize) - num := uintptr(last) - uintptr(first) - if num != metadataRawMemoryBytes { - t.Fatal("errr") - } - -} - -func Test_Syscall6(t *testing.T) { - /*arenaSizes := []uintptr{ - 512 << 20, - 256 << 20, - 128 << 20, - } - for _, arenaSize := range arenaSizes { - a, size := sysReserveAligned(unsafe.Pointer(p), arenaSize, heapArenaBytes) - if a != nil { - mheap_.arena.init(uintptr(a), size) - p = uintptr(a) + size // For hint below - break - } - }*/ - size := int(round(23232, 4096)) - addr := uintptr(sysReserve(size)) // 必须预留的多才可以 arena linearAlloc学习这个 - for i := 0; i < 8; i++ { - prot, flags, fd, offset, length := syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_FIXED|syscall.MAP_PRIVATE, -1, 0, size - r0, _, e1 := syscall.Syscall6(syscall.SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset)) - ret := r0 - var err error - if e1 != 0 { - err = errnoErr(e1) - } - if err != nil { - panic(err) - } - //fmt.Println(ret, err) - user := (*User)(unsafe.Pointer(ret)) - user.Age = 11 - user.Name = 13 - addr += uintptr(length) - fmt.Printf("user:%+v addr:%d\n", (*User)(unsafe.Pointer(ret)), ret) - } -} - -func Test_LineAlloc(t *testing.T) { - userSize, num := unsafe.Sizeof(User{}), 1000000000 - fmt.Println(heapRawMemoryBytes*8, unsafe.Sizeof(User{})*1000000000) - size := int(round(uintptr(heapRawMemoryBytes*8), 4096)) - addr := uintptr(sysReserve(size)) // 必须预留的多才可以 arena linearAlloc学习这个 - prot, flags, fd, offset, length := syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_FIXED|syscall.MAP_PRIVATE, -1, 0, size - r0, _, e1 := syscall.Syscall6(syscall.SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset)) - ret := r0 - var err error - if e1 != 0 { - err = errnoErr(e1) - } - if err != nil { - panic(err) - } - for i := 0; i < num; i++ { - user := (*User)(unsafe.Pointer(ret)) - user.Age = 11 - user.Name = 13 - //addr += uintptr(length) - ret += userSize - //fmt.Printf("user:%+v addr:%d\n", (*User)(unsafe.Pointer(ret)), ret) - } -} - -func Test_MMapAlloc(t *testing.T) { - arenaSizes := []uintptr{ - 512 << 20, - 256 << 20, - 128 << 20, - } - for i, size := range arenaSizes { - fmt.Println(i, size) - } - - userSize, num := unsafe.Sizeof(User{}), 1000000000 - size := int(round(userSize*uintptr(num), 4096)) - mem, err := syscall.Mmap(-1, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON /*|syscall.MAP_FIXED*/ |syscall.MAP_PRIVATE) - if err != nil { - t.Fatal(err) - } - ptr := unsafe.Pointer(&mem[0]) - ret := uintptr(ptr) - if err != nil { - panic(err) - } - for i := 0; i < num; i++ { - user := (*User)(unsafe.Pointer(ret)) - user.Age = 11 - user.Name = 13 - //addr += uintptr(length) - ret += userSize - //fmt.Printf("user:%+v addr:%d\n", (*User)(unsafe.Pointer(ret)), ret) - } -} - -func Test_GoAlloc(t *testing.T) { - t1 := time.Now() - us := make([]*User, 1000000000) - for i := 0; i < 1000000000; i++ { - user := &User{} - user.Age = 11 - user.Name = 13 - us[i] = user - } - fmt.Println(time.Now().Sub(t1)) -} - -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case syscall.EAGAIN: - return errEAGAIN - case syscall.EINVAL: - return errEINVAL - case syscall.ENOENT: - return errENOENT - } - return e -} - -func sysReserve(size int) unsafe.Pointer { - /*if raceenabled && GOOS == "darwin" { - // Currently the race detector expects memory to live within a certain - // range, and on Darwin 10.10 mmap is prone to ignoring hints, moreso - // than later versions and other BSDs (#26475). So, even though it's - // potentially dangerous to MAP_FIXED, we do it in the race detection - // case because it'll help maintain the race detector's invariants. - // - // TODO(mknyszek): Drop this once support for Darwin 10.10 is dropped, - // and reconsider this when #24133 is addressed. - flags |= _MAP_FIXED - }*/ - mem, err := syscall.Mmap(-1, 0, size, syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE) - if err != nil { - panic(err) - } - ptr := unsafe.Pointer(&mem[0]) - return ptr -} diff --git a/src/span.go b/src/span.go deleted file mode 100644 index a0f4583..0000000 --- a/src/span.go +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -// 写无锁atomic,扩容必须得全局锁 -type xSpan struct { - lock sync.Mutex - classIndex uint // class的索引 - classSize uintptr // classSpan的长度 - startAddr uintptr //bit索引 - npages uintptr - freeIndex uintptr - extensionPoint uintptr // 扩容负载因子 - - //span中可以分配的个数 - nelems uintptr - - //bitmap 每个bit标识地址是否被分配(用来分配计算空闲地址) - //1111111111111111111111111111111111111111111111111111111100000000 从右往左,遇到第一个1开始可以分配。 - allocCache uint64 - - //allocCache 保存的指针 - allocBits *gcBits - - //gc标记的bitmap(1为不需要的) - gcmarkBits *gcBits - - // gc - baseMask uint16 // if non-0, elemsize is a power of 2, & this will get object allocation base - divMul uint16 // for divide by elemsize - divMagic.mul - divShift uint8 // for divide by elemsize - divMagic.shift - divShift2 uint8 // for divide by elemsize - divMagic.shift2 - - allocCount uintptr - - next *xSpan - //pre *xSpan - heap *xHeap -} - -func (s *xSpan) layout() (size, n, total uintptr) { - total = s.npages << _PageShift - size = s.classSize - if size > 0 { - n = total / size - } - return -} -func (s *xSpan) Init(fact float32, heap *xHeap) error { - s.lock.Lock() - defer s.lock.Unlock() - _, n, _ := s.layout() - s.extensionPoint = uintptr(float32(n) * fact) - // Init the markbit structures - s.heap = heap - s.freeIndex = 0 - s.allocCache = ^uint64(0) // all 1s indicating all free. - s.nelems = n - var err error - s.gcmarkBits, err = newMarkBits(s.nelems, true) - if err != nil { - return err - } - s.allocBits, err = newAllocBits(s.nelems) - if index := s.classIndex; index == 0 { - s.divShift = 0 - s.divMul = 0 - s.divShift2 = 0 - s.baseMask = 0 - } else { - m := &class_to_divmagic[index] - s.divShift = m.shift - s.divMul = m.mul - s.divShift2 = m.shift2 - s.baseMask = m.baseMask - } - return err -} - -func (s *xSpan) freeOffset() (ptr uintptr, has bool) { - ptr = s.nextFreeFast() - if ptr == 0 { - return s.nextFree() - } - return ptr, true -} - -func (s *xSpan) needGrow() bool { - val := atomic.LoadUintptr(&s.allocCount) - return s.extensionPoint <= val -} - -func (s *xSpan) isFull() bool { - val := atomic.LoadUintptr(&s.allocCount) - return (val+1)*s.classSize >= _PageSize*s.npages -} - -//freeIndex增加: -// 1、首先使用自旋CAS获取空闲索引 -// 2、CAS失败,则锁加重为排他锁 -// 3、必须内存对齐 -func (s *xSpan) getFree(bitSize uintptr) (oldIndex uintptr, has bool) { - var swapped bool - for { - oldVal := atomic.LoadUintptr(&s.freeIndex) - // page对齐 - if aligned := (round(oldVal, _PageSize)) - oldVal; aligned < bitSize { - bitSize += aligned - } - if oldVal+bitSize > _PageSize*(s.npages) { - return 0, false - } - newVal := oldVal + bitSize - swapped = atomic.CompareAndSwapUintptr(&s.freeIndex, oldVal, newVal) - if swapped { - return oldVal, true - } - } -} - -func (s *xSpan) base() uintptr { - return s.startAddr -} - -func (s *xSpan) objIndex(p uintptr) uintptr { - byteOffset := p - s.base() - if byteOffset == 0 { - return 0 - } - if s.baseMask != 0 { - // s.baseMask is non-0, elemsize is a power of two, so shift by s.divShift - return byteOffset >> s.divShift - } - return uintptr(((uint64(byteOffset) >> s.divShift) * uint64(s.divMul)) >> s.divShift2) -} - -func (s *xSpan) markBitsForIndex(objIndex uintptr) markBits { - uint32p, mask := s.gcmarkBits.bitp(objIndex) - return markBits{uint32p, mask, objIndex} -} - -func (s *xSpan) setMarkBitsForIndex(objIndex uintptr) { - uint32p, mask := s.gcmarkBits.bitp(objIndex) - //fmt.Printf("xSpan:%d size:%d\n", uintptr(unsafe.Pointer(s)), s.classSize) - s.heap.addFreeCapacity(int64(s.classSize)) - markBits{uint32p, mask, objIndex}.setMarked() -} - -func (s *xSpan) markBitsForBase() markBits { - return markBits{(*uint32)(s.gcmarkBits), uint32(1), 0} -} - -//当前的allocCache中的最后一个bit空位和free位置。 -func (s *xSpan) nextFreeFast() uintptr { - if s.freeIndex >= s.nelems { - return 0 - } - s.lock.Lock() - defer s.lock.Unlock() - if s.freeIndex >= s.nelems { - return 0 - } - theBit := Ctz64(s.allocCache) // Is there a free object in the allocCache? - if theBit < 64 { - result := s.freeIndex + uintptr(theBit) - if result < s.nelems { - freeidx := result + 1 - if freeidx%64 == 0 && freeidx != s.nelems { - return 0 - } - s.allocCache >>= uint(theBit + 1) - s.freeIndex = freeidx - s.allocCount++ - return result*s.classSize + s.base() - } - } - return 0 -} - -func (s *xSpan) nextFree() (v uintptr, has bool) { - s.lock.Lock() - defer s.lock.Unlock() - offset, has := s.nextFreeIndex() - if has { - s.allocCount++ - return offset*s.classSize + s.base(), has - } - return -} - -//当前allocCache没该内容 -func (s *xSpan) nextFreeIndex() (uintptr, bool) { - sfreeindex := s.freeIndex - snelems := s.nelems - if sfreeindex >= snelems { - return sfreeindex, false - } - aCache := s.allocCache - bitIndex := Ctz64(aCache) - for bitIndex == 64 { - // Move index to start of next cached bits. - sfreeindex = (sfreeindex + 64) &^ (64 - 1) - if sfreeindex >= snelems { - s.freeIndex = snelems - return snelems, false - } - whichByte := sfreeindex / 32 - // Refill s.allocCache with the next 64 alloc bits. - s.refillAllocCache(whichByte) - aCache = s.allocCache - bitIndex = Ctz64(aCache) - } - result := sfreeindex + uintptr(bitIndex) - if result >= snelems { - s.freeIndex = snelems - return snelems, false - } - s.allocCache >>= uint(bitIndex + 1) - sfreeindex = result + 1 - if sfreeindex%64 == 0 && sfreeindex != snelems { - whichByte := sfreeindex / 32 - s.refillAllocCache(whichByte) - } - s.freeIndex = sfreeindex - return result, true -} - -func (s *xSpan) refillAllocCache(whichByte uintptr) { - bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.uint32p(whichByte))) - aCache := uint64(0) - aCache |= uint64(bytes[0]) - aCache |= uint64(bytes[1]) << (1 * 8) - aCache |= uint64(bytes[2]) << (2 * 8) - aCache |= uint64(bytes[3]) << (3 * 8) - aCache |= uint64(bytes[4]) << (4 * 8) - aCache |= uint64(bytes[5]) << (5 * 8) - aCache |= uint64(bytes[6]) << (6 * 8) - aCache |= uint64(bytes[7]) << (7 * 8) - s.allocCache = aCache -} - -//得到GC标记数目 -func (s *xSpan) countGcMarkBits() uintptr { - count := uintptr(0) - maxIndex := s.nelems / 32 - for i := uintptr(0); i < maxIndex; i++ { - mrkBits := s.gcmarkBits.uint32p(i) - for _, b := range *(*[4]byte)(unsafe.Pointer(mrkBits)) { - count += uintptr(oneBitCount[b]) - } - } - if bitsInLastByte := s.nelems % 32; bitsInLastByte != 0 { - mrkBits := *s.gcmarkBits.uint32p(maxIndex) - mask := uint32((1 << bitsInLastByte) - 1) - bits := mrkBits & mask - for _, b := range *(*[4]byte)(unsafe.Pointer(&bits)) { - count += uintptr(oneBitCount[b]) - } - } - return count -} diff --git a/src/span_pool.go b/src/span_pool.go deleted file mode 100644 index eceabf9..0000000 --- a/src/span_pool.go +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "fmt" - "log" - "reflect" - "sync" - "sync/atomic" - "unsafe" -) - -type SpanOperation int - -const ( - RemoveHead SpanOperation = -2 - ExpendAsync SpanOperation = -3 - ExpendSync SpanOperation = -4 -) - -type xSpanPool struct { - lock [_NumSizeClasses]*sync.RWMutex - inuse [_NumSizeClasses]uint64 - debug bool - spanGen [_NumSizeClasses]int32 // 小于0 正在扩容 - spans [_NumSizeClasses]*[]*xSpan //预分配,spans很短,不存在引用超长,第一个为当前正在使用的,第二个为预先分配的span - heap *xHeap - spanFact float32 - specialPageNumCoefficient [_NumSizeClasses]uint8 - //1750 + 950 - classSpan [_NumSizeClasses]*xClassSpan -} - -func newXSpanPool(heap *xHeap, spanFact float32) (*xSpanPool, error) { - sp := &xSpanPool{heap: heap, spanFact: spanFact, classSpan: heap.classSpan} - if err := sp.initLock(); err != nil { - return nil, err - } - return sp, nil -} - -func (sp *xSpanPool) initLock() error { - for i := 0; i < _NumSizeClasses; i++ { - var l sync.RWMutex - sp.lock[i] = &l - } - return nil -} - -func (sp *xSpanPool) allocClassSpan(index int) (ptr *xSpan, err error) { - pageNum := class_to_allocnpages[index] - size := class_to_size[index] - pageNum = uint8(Align(Align(uintptr(size), _PageSize)/uintptr(_PageSize), uintptr(pageNum))) - span, err := sp.classSpan[index].allocSpan(index, 0.75) - if err != nil { - return nil, err - } - return span, nil -} - -func (sp *xSpanPool) addInuse(sizeClass uint8) uint64 { - if !sp.debug { - return 0 - } - var swapped bool - for /*retry := 3; retry > 0; retry--*/ { - addr := &((sp.inuse)[sizeClass]) - oldVal := atomic.LoadUint64(addr) - newVal := oldVal + 1 - swapped = atomic.CompareAndSwapUint64(addr, oldVal, newVal) - if swapped { - return newVal - } - } -} -func (sp *xSpanPool) AllocSlice(eleSize uintptr, cap, len uintptr) (p unsafe.Pointer, err error) { - sl, err := sp.Alloc(eleSize*cap + unsafe.Sizeof(reflect.SliceHeader{})) - if err != nil { - return nil, err - } - data := uintptr(sl) + unsafe.Sizeof(reflect.SliceHeader{}) - sh := (*reflect.SliceHeader)(sl) - sh.Cap = int(cap) - sh.Data = data - sh.Len = int(len) - return sl, nil -} - -var is bool - -//通过增加key、value长度使得分配到不同span -//todo 擦除数据 -func (sp *xSpanPool) Alloc(size uintptr) (p unsafe.Pointer, err error) { - if size > _MaxSmallSize { - pageNum := Align(size, _PageSize) / _PageSize - chunk, err := sp.heap.allocRawSpan(pageNum) - if err != nil { - return nil, err - } - if err := chunk.Init(sp.spanFact, sp.heap); err != nil { - return nil, err - } - chunk.allocCount = 1 - chunk.nelems = 1 - sp.classSpan[0].releaseSpan(chunk) - return unsafe.Pointer(chunk.startAddr), nil - } - var sizeclass uint8 - if size <= smallSizeMax-8 { - sizeclass = size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv] - } else { - sizeclass = size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv] - } - size = uintptr(class_to_size[sizeclass]) - spans, spanGen := sp.getSpan(sizeclass) - var ptr, idex uintptr - var has, needGrow bool - for i, span := range spans { - if span == nil { - continue - } - if ptr, has = span.freeOffset(); has { - if err := sp.clear(ptr, size); err != nil { - log.Printf("xSpanPool.Alloc clear err:%s", err) - } - needGrow = span.needGrow() - idex = uintptr(i) - break - } - } - if needGrow { - if _, need, _ := sp.needExpendAsync(sizeclass, ExpendAsync); need { - go sp.growSpan(sizeclass, ExpendAsync, spanGen) - } - } - if idex < 1 && has { - sp.addInuse(sizeclass) - return unsafe.Pointer(ptr), nil - } - if idex > 0 && has { - //idx前已经使用完了,删除前面满了的。 - sp.addInuse(sizeclass) - sp.growSpan(sizeclass, RemoveHead, spanGen) - return unsafe.Pointer(ptr), nil - } - // 同步扩容 - if !has { - //所有的span都没有空位,需要阻塞同步分配 产生新的class span,释放所有的span - if err := sp.growSpan(sizeclass, ExpendSync, spanGen); err != nil { - return nil, err - } - return sp.Alloc(size) - } - return nil, fmt.Errorf("idex:%d has:%t is err", idex, has) -} - -func (sp *xSpanPool) clear(ptr, size uintptr) (err error) { - dst := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: ptr, Len: int(size), Cap: int(size)})) - if length := copy(dst, make([]byte, size)); int(size) != length { - return fmt.Errorf("xSpanPool.clear copy err:copy len is err") - } - return -} - -/** -1、同步增、异步增、删除等方式 - todo 考虑并发扩容的问题(异步线程数控制) -*/ -func (sp *xSpanPool) needExpendAsync(sizeClass uint8, op SpanOperation) (int32, bool, error) { - if op != ExpendAsync { - return -1, false, errors.New("not ExpendAsync") - } - addr := &sp.spanGen[sizeClass] - currentSpanGen, newValue := atomic.LoadInt32(addr), op - if currentSpanGen < 0 { - return currentSpanGen, false, nil - } - if !atomic.CompareAndSwapInt32(addr, currentSpanGen, int32(newValue)) { - return currentSpanGen, false, nil - } - return currentSpanGen, true, nil -} - -func (sp *xSpanPool) growSpan(sizeClass uint8, op SpanOperation, spanGen int32) error { - sp.lock[sizeClass].Lock() - defer sp.lock[sizeClass].Unlock() - addr := (*unsafe.Pointer)(unsafe.Pointer(&sp.spans[sizeClass])) - val := atomic.LoadPointer(addr) - var span []*xSpan - if val != nil { - span = *(*[]*xSpan)(val) - } - inuse := sp.inuse[sizeClass] - if spanGen < 0 { - spanGen = 1 - } - switch op { - case RemoveHead: - var newIdex int - if len(span) < 1 || !span[0].isFull() { - return nil - } - sp.classSpan[sizeClass].releaseSpan(span[0]) - arr := span[1:] - atomic.StorePointer(addr, unsafe.Pointer(&arr)) - sp.spanGen[sizeClass] = spanGen + 1 - if sp.debug { - log.Printf("INFO modifySpan RemoveHead sizeClass: %d inuse:%d spanGen:%d newIdex:%d\n", sizeClass, inuse, spanGen, newIdex) - } - return nil - case ExpendAsync: - if len(span) > 0 && !span[len(span)-1].needGrow() { - return nil - } - if err := sp.growClassSpan(int(sizeClass), span); err != nil { - log.Printf("ERR classSpan err, err: %s\n", err) - } - if sp.debug { - log.Printf("INFO modifySpan ExpendAsync sizeClass: %d inuse:%d spanGen:%d newspanGen:%d\n", sizeClass, inuse, spanGen, spanGen+1) - } - sp.spanGen[sizeClass] = spanGen + 1 - return nil - case ExpendSync: - if len(span) > 0 && !span[len(span)-1].needGrow() { - return nil - } - if err := sp.growClassSpan(int(sizeClass), span); err != nil { - return err - } - sp.spanGen[sizeClass] = spanGen + 1 - if sp.debug { - log.Printf("INFO modifySpan ExpendSync sizeClass: %d inuse:%d spanGen:%d newspanGen:%d\n", sizeClass, inuse, spanGen, spanGen+1) - } - return nil - default: - log.Println("ERR modifySpan modifySpan err: op is not support") - return fmt.Errorf("op[%d] is not support", op) - } -} - -func (sp *xSpanPool) getSpan(sizeClass uint8) (spans []*xSpan, spanGen int32) { - addr := (*unsafe.Pointer)(unsafe.Pointer(&sp.spans[sizeClass])) - val := atomic.LoadPointer(addr) - if val == nil { - return - } - spans = *(*[]*xSpan)(val) - spanGen = sp.spanGen[sizeClass] - return spans, spanGen -} - -func (sp *xSpanPool) growClassSpan(sizeClass int, spans []*xSpan) error { - span, err := sp.allocClassSpan(sizeClass) - if err != nil { - return err - } - length := len(spans) - newSpans := make([]*xSpan, length, length+1) - if copy(newSpans, spans) != length { - return fmt.Errorf("copy err") - } - newSpans = append(newSpans, span) - addr := (*unsafe.Pointer)(unsafe.Pointer(&sp.spans[sizeClass])) - atomic.StorePointer(addr, unsafe.Pointer(&newSpans)) - return nil -} - -func (sp *xSpanPool) Copy2(item1 []byte, item2 []byte) (newItem1 []byte, newItem2 []byte, err error) { - item1Size, item2Size := len(item1), len(item2) - rawSize := item1Size + item2Size - if rawSize < 1 { - return nil, nil, nil - } - dataPtr, err := sp.Alloc(uintptr(rawSize)) - if err != nil { - return nil, nil, err - } - dst := reflect.SliceHeader{Data: uintptr(dataPtr), Len: 0, Cap: rawSize} - dstBytes := *(*[]byte)(unsafe.Pointer(&dst)) - dstBytes = append(dstBytes, item1...) - dstBytes = append(dstBytes, item2...) - dst.Len = item1Size - dst.Cap = item1Size - str1 := *(*[]byte)(unsafe.Pointer(&dst)) - dst.Len = item2Size - dst.Cap = item2Size - dst.Data = uintptr(dataPtr) + uintptr(item1Size) - str2 := *(*[]byte)(unsafe.Pointer(&dst)) - return str1, str2, err -} - -var TestBbulks uintptr - -func (sp *xSpanPool) Free(addr uintptr) error { - return sp.heap.free(addr) -} - -func newXConcurrentHashMapSpanPool(heap *xHeap, spanFact float32, pageNumCoefficient uint8) (*xSpanPool, error) { - sp := &xSpanPool{heap: heap, spanFact: spanFact, classSpan: heap.classSpan} - sp.specialPageNumCoefficient[1], sp.specialPageNumCoefficient[2], sp.specialPageNumCoefficient[4], sp.specialPageNumCoefficient[6] = - pageNumCoefficient*10, pageNumCoefficient*5, pageNumCoefficient*1, pageNumCoefficient*1 - if err := sp.initLock(); err != nil { - return nil, err - } - return sp, nil -} - -//启动利用class_to_allocnpages 预先分配span。 -//alloc超过阈值,异步预分配 -//alloc没有空闲时候,同步分配(防止分配太多)。 diff --git a/src/span_pool_test.go b/src/span_pool_test.go deleted file mode 100644 index 474c7fe..0000000 --- a/src/span_pool_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "sync" - "sync/atomic" - "testing" - "time" - "unsafe" -) - -func TestPoolAlloc(t *testing.T) { - h, err := newXHeap() - if err != nil { - t.Fatal(err) - } - sp, err := newXSpanPool(h, 0.75) - if err != nil { - t.Fatal(err) - } - sp.debug = true - var wait sync.WaitGroup - wait.Add(20) - for i := 0; i < 20; i++ { - go func() { - defer wait.Done() - for i := 0; i < 1000; i++ { - sp.Alloc(4) - } - }() - } - wait.Wait() -} - -func TestSpanLock(t *testing.T) { - h, err := newXHeap() - if err != nil { - t.Fatal(err) - } - sp, err := newXSpanPool(h, 0.75) - if err != nil { - t.Fatal(err) - } - sp.debug = true - cs, err := sp.allocClassSpan(1) - if err != nil { - t.Fatal(err) - } - var wait sync.WaitGroup - cnt := _PageSize / 4 - allcCnt := uintptr(20 * (cnt / 20)) - wait.Add(20) - for i := 0; i < 20; i++ { - go func() { - defer wait.Done() - for j := 0; j < cnt/20; j++ { - cs.getFree(4) - } - }() - } - wait.Wait() - fmt.Printf("%+v \nfreeIndex:%d %d %d \n", cs, cs.freeIndex, allcCnt*4, allcCnt) - //todo 问题,好像自己实现的锁有问题,应该是CAS 和 悲观锁冲突了。只能选一个。cas和锁必须互斥 - // 锁里面有CAS,是否考虑用锁呢? - <-time.After(time.Second) - if val := atomic.LoadUintptr(&cs.freeIndex); val != allcCnt*4 { - panic(val) - } -} - -func Test_SanPool_Alloc(t *testing.T) { - f := &Factory{} - mm, err := f.CreateConcurrentHashMapMemory(0.6, 1) - if err != nil { - panic(err) - } - key, val := "key", "val" - uSize := unsafe.Sizeof(User{}) - t1 := time.Now() - us := make([]*User, 1000000) - - for i := 0; i < 1000000; i++ { - entryPtr, err := mm.Alloc(uSize) - if err != nil { - panic(err) - } - user := (*User)(entryPtr) - f1(user, key, val) - us[i] = user - } - fmt.Println(time.Now().Sub(t1), len(us)) - for _, u := range us { - if u.Addr != "key" { - panic(u) - } - } -} - -func Test_SanPool_Find(t *testing.T) { - h, err := newXHeap() - if err != nil { - t.Fatal(err) - } - sp, err := newXSpanPool(h, 0.75) - if err != nil { - t.Fatal(err) - } - key, val := "key", "val" - uSize := unsafe.Sizeof(User{}) - usPtr := make([]uintptr, 100000) - us := make([]*User, 100000) - - for i := 0; i < 100000; i++ { - entryPtr, err := sp.Alloc(uSize) - if err != nil { - panic(err) - } - user := (*User)(entryPtr) - f1(user, key, val) - usPtr[i] = uintptr(entryPtr) - us[i] = user - } - for i := 0; i < 50000; i++ { - u := usPtr[i] - if err := h.free(u); err != nil { - t.Fatal(i, err) - } - } - fmt.Println("第一次清空结束") - - for i := 0; i < 50000; i++ { - u := usPtr[i] - if err := h.free(u); err != nil { - t.Fatal(i, err) - } - } - fmt.Println("第二次清空结束") - for i := 50000; i < 100000; i++ { - u := usPtr[i] - if err := h.free(u); err != nil { - t.Fatal(i, err) - } - } - - fmt.Println("大span清空结束") - - size := uintptr(_MaxSmallSize + 12) - p, err := sp.Alloc(size) - if err != nil { - panic(err) - } - if err := h.free(uintptr(p)); err != nil { - t.Fatal(err) - } - if err := h.free(uintptr(p)); err != nil { - t.Fatal(err) - } -} diff --git a/src/span_test.go b/src/span_test.go deleted file mode 100644 index 153eac9..0000000 --- a/src/span_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "testing" -) - -func TestFreeOffset(t *testing.T) { - span := &xSpan{} - span.freeIndex = 2 - span.npages = 4 - span.classSize = uintptr(class_to_size[span.freeIndex]) - - span.startAddr = 1 - span.Init(0.75, nil) - for i := 0; i < 700; i++ { - ptr, has := span.freeOffset() - if !has && uintptr(i) <= span.npages*(_PageSize)/(span.classSize)-1 { - t.Fatal("+++++", ptr, has) - } - if has && uintptr(i) > (span.npages)*uintptr(_PageSize)/(span.classSize)-1 { - t.Fatal("+++++", ptr, has) - } - } -} - -func TestGcFreeOffset(t *testing.T) { - span := &xSpan{} - span.freeIndex = 2 - span.npages = 1 - span.classSize = uintptr(class_to_size[span.freeIndex]) - span.startAddr = 1 - span.Init(0.75, nil) - - bit := span.gcmarkBits - objIndexs := []int{1, 6, 3, 2, 10, 80} - for _, index := range objIndexs { - objIndex := uintptr(index) - 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() - } - span.allocBits = span.gcmarkBits - span.refillAllocCache(0) - max := span.npages*(_PageSize)/(span.classSize) - 1 - uintptr(len(objIndexs)) - hasOffset := 0 - for i := 0; i < 700; i++ { - ptr, has := span.freeOffset() - if !has && uintptr(i) <= max { - t.Fatal("+++++", ptr, has) - } - if has && uintptr(i) > max { - t.Fatal("-----", ptr, has) - } - if has { - hasOffset = i - } - } - fmt.Println(span.npages*(_PageSize)/(span.classSize), hasOffset, span.npages*(_PageSize)/(span.classSize)-1-uintptr(hasOffset)) -} diff --git a/src/string_alloc.go b/src/string_alloc.go deleted file mode 100644 index 33a16f2..0000000 --- a/src/string_alloc.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "fmt" - "reflect" - "unsafe" -) - -type xStringAllocator struct { - sp spanPool -} - -func newXStringAllocator(sp spanPool) *xStringAllocator { - return &xStringAllocator{sp: sp} -} - -func (sa *xStringAllocator) alloc(size uintptr) (sh *reflect.StringHeader, err error) { - if size < 0 { - return nil, errors.New("size == 0") - } - p, err := sa.sp.Alloc(unsafe.Sizeof(reflect.StringHeader{})) - if err != nil { - return nil, err - } - sh = (*reflect.StringHeader)(p) - sh.Len = int(size) - dataPtr, err := sa.sp.Alloc(size) - if err != nil { - return nil, err - } - sh.Data = uintptr(dataPtr) - return sh, nil -} - -//todo 这里有bug,StringHeader没有计算进去,应该使用[]byte -func (sa *xStringAllocator) FromInAddr(addr uintptr, contents ...string) (p []*string, err error) { - offset := addr - p = make([]*string, len(contents)) - for i, content := range contents { - size := uintptr(len(content)) - sh := (*reflect.StringHeader)(unsafe.Pointer(offset)) - sh.Len = int(size) - offset += unsafe.Sizeof(reflect.StringHeader{}) - - sh.Data = offset - offset += size - dst := reflect.SliceHeader{Data: sh.Data, Len: sh.Len, Cap: sh.Len} - dstBytes := *(*[]byte)(unsafe.Pointer(&dst)) - sourcePtr := (*reflect.StringHeader)(unsafe.Pointer(&content)) - source := reflect.SliceHeader{Data: sourcePtr.Data, Len: sourcePtr.Len, Cap: sourcePtr.Len} - srcByte := *(*[]byte)(unsafe.Pointer(&source)) - if len := copy(dstBytes, srcByte); len != sh.Len { - return nil, fmt.Errorf("copy length is err: len = %d\n", len) - } - p[i] = (*string)(unsafe.Pointer(sh)) - } - return p, nil -} - -func (sa *xStringAllocator) From2(item1 string, item2 string) (newItem1 string, newItem2 string, err error) { - raw := item1 + item2 - rawSize, item1Size, item2Size := len(raw), len(item1), len(item2) - if rawSize < 1 { - return "", "", nil - } - dataPtr, err := sa.sp.Alloc(uintptr(rawSize)) - if err != nil { - return "", "", err - } - dst := reflect.SliceHeader{Data: uintptr(dataPtr), Len: rawSize, Cap: rawSize} - dstBytes := *(*[]byte)(unsafe.Pointer(&dst)) - if len := copy(dstBytes, raw); len != rawSize { - return "", "", fmt.Errorf("copy length is err: len = %d\n", len) - } - dst.Len = item1Size - str1 := *(*string)(unsafe.Pointer(&dst)) - dst.Len = item2Size - dst.Data = uintptr(dataPtr) + uintptr(item1Size) - str2 := *(*string)(unsafe.Pointer(&dst)) - return str1, str2, err -} - -func (sa *xStringAllocator) From(content string) (p string, err error) { - size := uintptr(len(content)) - dataPtr, err := sa.sp.Alloc(size) - if err != nil { - return "", err - } - length := int(size) - dst := reflect.SliceHeader{Data: uintptr(dataPtr), Len: length, Cap: length} - dstBytes := *(*[]byte)(unsafe.Pointer(&dst)) - if len := copy(dstBytes, content); len != length { - return "", fmt.Errorf("copy length is err: len = %d\n", len) - } - return *(*string)(unsafe.Pointer(&dst)), err -} - - -func (sa *xStringAllocator) FreeString(content string)error{ - sh := (*reflect.StringHeader)(unsafe.Pointer(&content)) - return sa.sp.Free(sh.Data) -} - diff --git a/src/trees.go b/src/trees.go deleted file mode 100644 index c1f985b..0000000 --- a/src/trees.go +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "math/rand" -) - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Page heap. -// -// See malloc.go for the general overview. -// -// Large spans are the subject of this file. Spans consisting of less than -// _MaxMHeapLists are held in lists of like sized spans. Larger spans -// are held in a treap. See https://en.wikipedia.org/wiki/Treap or -// https://faculty.washington.edu/aragon/pubs/rst89.pdf for an overview. -// sema.go also holds an implementation of a treap. -// -// Each treapNode holds a single span. The treap is sorted by page size -// and for spans of the same size a secondary sort based on start address -// is done. -// Spans are returned based on a best fit algorithm and for spans of the same -// size the one at the lowest address is selected. -// -// The primary routines are -// insert: adds a span to the treap -// remove: removes the span from that treap that best fits the required size -// removeSpan: which removes a specific span from the treap -// -// _mheap.lock must be held when manipulating this data structure. - -var EmptyError = errors.New("not found") - -type xTreap struct { - treap *treapNode - valAllocator *xAllocator -} - -func newXTreap(valAllocator *xAllocator) *xTreap { - return &xTreap{valAllocator: valAllocator} -} - -func (s *xChunk) base() uintptr { - return s.startAddr -} - -type treapNode struct { - right *treapNode // all treapNodes > this treap node - left *treapNode // all treapNodes < this treap node - parent *treapNode // direct parent of this node, nil if root - npagesKey uintptr // number of pages in spanKey, used as primary sort key - priority uint32 // random number used by treap algorithm to keep tree probabilistically balanced - chunk *xChunk -} - -func (t *treapNode) pred() (*treapNode, error) { - if t.left != nil { - // If it has a left child, its predecessor will be - // its right most left (grand)child. - t = t.left - for t.right != nil { - t = t.right - } - return t, nil - } - // If it has no left child, its predecessor will be - // the first grandparent who's right child is its - // ancestor. - // - // We compute this by walking up the treap until the - // current node's parent is its parent's right child. - // - // If we find at any point walking up the treap - // that the current node doesn't have a parent, - // we've hit the root. This means that t is already - // the left-most node in the treap and therefore - // has no predecessor. - for t.parent != nil && t.parent.right != t { - if t.parent.left != t { - println("runtime: predecessor t=", t, "t.chunk=", t.chunk) - return nil, errors.New("node is not its parent's child") - } - t = t.parent - } - return t.parent, nil -} - -func (t *treapNode) succ() (*treapNode, error) { - if t.right != nil { - // If it has a right child, its successor will be - // its left-most right (grand)child. - t = t.right - for t.left != nil { - t = t.left - } - return t, nil - } - // See pred. - for t.parent != nil && t.parent.left != t { - if t.parent.right != t { - println("runtime: predecessor t=", t, "t.chunk=", t.chunk) - return nil, errors.New("node is not its parent's child") - } - t = t.parent - } - return t.parent, nil -} - -// isSpanInTreap is handy for debugging. One should hold the heap lock, usually -// mheap_.lock(). -func (t *treapNode) isChunkInTreap(s *xChunk) bool { - if t == nil { - return false - } - return t.chunk == s || t.left.isChunkInTreap(s) || t.right.isChunkInTreap(s) -} - -// walkTreap is handy for debugging. -// Starting at some treapnode t, for example the root, do a depth first preorder walk of -// the tree executing fn at each treap node. One should hold the heap lock, usually -// mheap_.lock(). -func (t *treapNode) walkTreap(fn func(tn *treapNode)) { - if t == nil { - return - } - fn(t) - t.left.walkTreap(fn) - t.right.walkTreap(fn) -} - -// checkTreapNode when used in conjunction with walkTreap can usually detect a -// poorly formed treap. -func checkTreapNode(t *treapNode) error { - // lessThan is used to order the treap. - // npagesKey and npages are the primary keys. - // spanKey and span are the secondary keys. - // span == nil (0) will always be lessThan all - // spans of the same size. - lessThan := func(npages uintptr, chunk *xChunk) bool { - if t.npagesKey != npages { - return t.npagesKey < npages - } - // t.npagesKey == npages - return t.chunk.base() < chunk.base() - } - - if t == nil { - return nil - } - if t.chunk.npages != t.npagesKey { - println("runtime: checkTreapNode treapNode t=", t, " t.npagesKey=", t.npagesKey, - "t.chunk.npages=", t.chunk.npages) - return errors.New("span.npages and treap.npagesKey do not match") - } - if t.left != nil && lessThan(t.left.npagesKey, t.left.chunk) { - return errors.New("t.lessThan(t.left.npagesKey, t.left.chunk) is not false") - } - if t.right != nil && !lessThan(t.right.npagesKey, t.right.chunk) { - return errors.New("!t.lessThan(t.left.npagesKey, t.left.chunk) is not false") - } - return nil -} - -// treapIter is a bidirectional iterator type which may be used to iterate over a -// an xTreap in-order forwards (increasing order) or backwards (decreasing order). -// Its purpose is to hide details about the treap from users when trying to iterate -// over it. -// -// To create iterators over the treap, call start or end on an xTreap. -type treapIter struct { - t *treapNode -} - -// span returns the span at the current position in the treap. -// If the treap is not valid, span will panic. -func (i *treapIter) span() *xChunk { - return i.t.chunk -} - -// valid returns whether the iterator represents a valid position -// in the xTreap. -func (i *treapIter) valid() bool { - return i.t != nil -} - -// next moves the iterator forward by one. Once the iterator -// ceases to be valid, calling next will panic. -func (i treapIter) next() (treapIter, error) { - var err error - i.t, err = i.t.succ() - return i, err -} - -// prev moves the iterator backwards by one. Once the iterator -// ceases to be valid, calling prev will panic. -func (i treapIter) prev() (treapIter, error) { - var err error - i.t, err = i.t.pred() - return i, err -} - -// start returns an iterator which points to the start of the treap (the -// left-most node in the treap). -func (root *xTreap) start() treapIter { - t := root.treap - if t == nil { - return treapIter{} - } - for t.left != nil { - t = t.left - } - return treapIter{t: t} -} - -// end returns an iterator which points to the end of the treap (the -// right-most node in the treap). -func (root *xTreap) end() treapIter { - t := root.treap - if t == nil { - return treapIter{} - } - for t.right != nil { - t = t.right - } - return treapIter{t: t} -} - -// insert adds span to the large span treap. -func (root *xTreap) insert(chunk *xChunk) error { - npages := chunk.npages - var last *treapNode - pt := &root.treap - for t := *pt; t != nil; t = *pt { - last = t - if t.npagesKey < npages { - pt = &t.right - } else if t.npagesKey > npages { - pt = &t.left - } else if t.chunk.base() < chunk.base() { - // t.npagesKey == npages, so sort on span addresses. - pt = &t.right - } else if t.chunk.base() > chunk.base() { - pt = &t.left - } else { - return errors.New("inserting span already in treap") - } - } - - // Add t as new leaf in tree of span size and unique addrs. - // The balanced tree is a treap using priority as the random heap priority. - // That is, it is a binary tree ordered according to the npagesKey, - // but then among the space of possible binary trees respecting those - // npagesKeys, it is kept balanced on average by maintaining a heap ordering - // on the priority: s.priority <= both s.right.priority and s.right.priority. - // https://en.wikipedia.org/wiki/Treap - // https://faculty.washington.edu/aragon/pubs/rst89.pdf - - ptr, err := root.valAllocator.alloc() - if err != nil { - return err - } - t := (*treapNode)(ptr) - t.npagesKey = chunk.npages - t.priority = rand.Uint32() - t.chunk = chunk - t.left = nil - t.right = nil - t.parent = nil - t.parent = last - *pt = t // t now at a leaf. - - // Rotate up into tree according to priority. - for t.parent != nil && t.parent.priority > t.priority { - if t != nil && t.chunk.npages != t.npagesKey { - println("runtime: insert t=", t, "t.npagesKey=", t.npagesKey) - println("runtime: t.chunk=", t.chunk, "t.chunk.npages=", t.chunk.npages) - return errors.New("span and treap sizes do not match?") - } - if t.parent.left == t { - root.rotateRight(t.parent) - } else { - if t.parent.right != t { - return errors.New("treap insert finds a broken treap") - } - root.rotateLeft(t.parent) - } - } - return nil -} - -func (root *xTreap) removeNode(t *treapNode) error { - if t.chunk.npages != t.npagesKey { - return errors.New("span and treap node npages do not match") - } - // Rotate t down to be leaf of tree for removal, respecting priorities. - for t.right != nil || t.left != nil { - if t.right == nil || t.left != nil && t.left.priority < t.right.priority { - root.rotateRight(t) - } else { - root.rotateLeft(t) - } - } - // Remove t, now a leaf. - if t.parent != nil { - if t.parent.left == t { - t.parent.left = nil - } else { - t.parent.right = nil - } - } else { - root.treap = nil - } - // Return the found treapNode's span after freeing the treapNode. - //mheap_.treapalloc.free(unsafe.Pointer(t)) - return nil -} - -// find searches for, finds, and returns the treap node containing the -// smallest span that can hold npages. If no span has at least npages -// it returns nil. -// This is a simple binary tree search that tracks the best-fit node found -// so far. The best-fit node is guaranteed to be on the path to a -// (maybe non-existent) lowest-base exact match. -func (root *xTreap) find(npages uintptr) (*treapNode, error) { - var best *treapNode - t := root.treap - for t != nil { - if t.chunk == nil { - return nil, errors.New("treap node with nil spanKey found") - } - // If we found an exact match, try to go left anyway. There could be - // a span there with a lower base address. - // - // Don't bother checking nil-ness of left and right here; even if t - // becomes nil, we already know the other path had nothing better for - // us anyway. - if t.npagesKey >= npages { - best = t - t = t.left - } else { - t = t.right - } - } - if best == nil { - return nil, EmptyError - } - return best, nil -} - -// removeSpan searches for, finds, deletes span along with -// the associated treap node. If the span is not in the treap -// then t will eventually be set to nil and the t.chunk -// will throw. -func (root *xTreap) removeChunk(chunk *xChunk) error { - npages := chunk.npages - t := root.treap - for t.chunk != chunk { - if t.npagesKey < npages { - t = t.right - } else if t.npagesKey > npages { - t = t.left - } else if t.chunk.base() < chunk.base() { - t = t.right - } else if t.chunk.base() > chunk.base() { - t = t.left - } - } - return root.removeNode(t) -} - -// erase removes the element referred to by the current position of the -// iterator. This operation consumes the given iterator, so it should no -// longer be used. It is up to the caller to get the next or previous -// iterator before calling erase, if need be. -func (root *xTreap) erase(i treapIter) { - root.removeNode(i.t) -} - -// rotateLeft rotates the tree rooted at node x. -// turning (x a (y b c)) into (y (x a b) c). -func (root *xTreap) rotateLeft(x *treapNode) error { - // p -> (x a (y b c)) - p := x.parent - a, y := x.left, x.right - b, c := y.left, y.right - - y.left = x - x.parent = y - y.right = c - if c != nil { - c.parent = y - } - x.left = a - if a != nil { - a.parent = x - } - x.right = b - if b != nil { - b.parent = x - } - - y.parent = p - if p == nil { - root.treap = y - } else if p.left == x { - p.left = y - } else { - if p.right != x { - return errors.New("large span treap rotateLeft") - } - p.right = y - } - return nil -} - -// rotateRight rotates the tree rooted at node y. -// turning (y (x a b) c) into (x a (y b c)). -func (root *xTreap) rotateRight(y *treapNode) error { - // p -> (y (x a b) c) - p := y.parent - x, c := y.left, y.right - a, b := x.left, x.right - - x.left = a - if a != nil { - a.parent = x - } - x.right = y - y.parent = x - y.left = b - if b != nil { - b.parent = y - } - y.right = c - if c != nil { - c.parent = y - } - - x.parent = p - if p == nil { - root.treap = x - } else if p.left == y { - p.left = x - } else { - if p.right != y { - return errors.New("large span treap rotateRight") - } - p.right = x - } - return nil -} diff --git a/src/xmm.go b/src/xmm.go deleted file mode 100644 index cbf2358..0000000 --- a/src/xmm.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "errors" - "fmt" - "unsafe" -) - -var NilError = errors.New("params is illegal") - -type spanPool interface { - Alloc(byteSize uintptr) (p unsafe.Pointer, err error) - AllocSlice(eleSize uintptr, cap, len uintptr) (p unsafe.Pointer, err error) - Free(addr uintptr) error - Copy2(item1 []byte, item2 []byte) (newItem1 []byte, newItem2 []byte, err error) -} - -type stringAllocator interface { - From(content string) (p string, err error) - From2(item1 string, item2 string) (newItem1 string, newItem2 string, err error) - FromInAddr(addr uintptr, contents ...string) (p []*string, err error) - FreeString(content string) error -} - -type Chunk struct { - StartAddr uintptr - Npages uintptr -} - -type XMemory interface { - spanPool - stringAllocator - RawAlloc(pageNum uintptr) (p *Chunk, err error) - GetPageSize() uintptr -} - -type mm struct { - sp spanPool - sa stringAllocator - h *xHeap -} - -func (m *mm) Copy2(item1 []byte, item2 []byte) (newItem1 []byte, newItem2 []byte, err error) { - return m.sp.Copy2(item1, item2) -} - -func (m *mm) Alloc(byteSize uintptr) (p unsafe.Pointer, err error) { - if byteSize < 1 { - return nil, NilError - } - return m.sp.Alloc(byteSize) -} - -func (m *mm) AllocSlice(eleSize uintptr, cap, len uintptr) (p unsafe.Pointer, err error) { - if eleSize < 1 || cap < 1 { - return nil, NilError - } - return m.sp.AllocSlice(eleSize, cap, len) -} - -func (m *mm) From(content string) (p string, err error) { - if len(content) < 1 { - return "", NilError - } - return m.sa.From(content) -} - -func (m *mm) From2(item1 string, item2 string) (newItem1 string, newItem2 string, err error) { - return m.sa.From2(item1, item2) -} - -func (m *mm) FromInAddr(addr uintptr, contents ...string) (p []*string, err error) { - if addr < 1 { - return p, NilError - } - return m.sa.FromInAddr(addr, contents...) -} - -func (m *mm) RawAlloc(pageNum uintptr) (p *Chunk, err error) { - if pageNum < 1 { - return p, NilError - } - if c, err := m.h.allocRawSpan(pageNum); err != nil { - return nil, err - } else { - return &Chunk{StartAddr: c.startAddr, Npages: c.npages}, nil - } -} - -func (m *mm) Free(addr uintptr) error { - if addr < 1 { - return NilError - } - return m.sp.Free(addr) -} - -func (m *mm) FreeString(content string) error { - return m.sa.FreeString(content) -} - - - -func (m *mm) GetPageSize() uintptr { - return _PageSize -} - -type Factory struct { - sp *xSpanPool -} - -func (s *Factory) CreateMemory(spanFact float32) (XMemory, error) { - if spanFact <= 0 { - return nil, NilError - } - h, err := newXHeap() - if err != nil { - return nil, err - } - sp, err := newXSpanPool(h, spanFact) - if err != nil { - return nil, err - } - s.sp = sp - sa := newXStringAllocator(sp) - return &mm{sp: sp, sa: sa, h: h}, nil -} - -//NewXConcurrentHashMapSpanPool -func (s *Factory) CreateConcurrentHashMapMemory(spanFact float32, pageNumCoefficient uint8) (XMemory, error) { - if spanFact <= 0 { - return nil, NilError - } - h, err := newXHeap() - if err != nil { - return nil, err - } - sp, err := newXConcurrentHashMapSpanPool(h, spanFact, pageNumCoefficient) - if err != nil { - return nil, err - } - sa := newXStringAllocator(sp) - s.sp = sp - return &mm{sp: sp, sa: sa, h: h}, nil -} - -func (s *Factory) PrintStatus() { - for index, u := range s.sp.inuse { - if u < 100 { - continue - } - pageNum := class_to_allocnpages[index] - size := class_to_size[index] - pageNum = uint8(Align(Align(uintptr(size), _PageSize)/uintptr(_PageSize), uintptr(pageNum))) - fmt.Println(index, u, uintptr(pageNum)*_PageSize/uintptr(size)) - } -} - diff --git a/src/xmm_test.go b/src/xmm_test.go deleted file mode 100644 index f01e6b6..0000000 --- a/src/xmm_test.go +++ /dev/null @@ -1,720 +0,0 @@ -// Copyright (c) 2022 XMM project Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// XMM Project Site: https://github.com/heiyeluren -// XMM URL: https://github.com/heiyeluren/XMM -// - -package xmm - -import ( - "fmt" - "github.com/spf13/cast" - "log" - "math/rand" - "net/http" - _ "net/http/pprof" // 会自动注册 handler 到 http server,方便通过 http 接口获取程序运行采样报告 - "os" - "reflect" - "runtime" - "strconv" - "sync" - "testing" - "time" - "unsafe" -) - -func TestName(t *testing.T) { - fmt.Println(heapRawMemoryBytes, _PageSize, pagesPerRawMemory, logHeapRawMemoryBytes, RawMemoryL1Bits, RawMemoryL2Bits) - fmt.Println(heapAddrBits, logHeapRawMemoryBytes, RawMemoryL1Bits, 1<<28) - fmt.Println(1< 0 && uintptr(p)-pre != 128 { - t.Log("重新分配了一个span地址") - } else { - t.Log("同一个span中") - } - pre = uintptr(unsafe.Pointer(p)) - v := *(p)*/ - fmt.Println(p) - } -} - -func TestUser(t *testing.T) { - h, err := newXHeap() - if err != nil { - t.Fatal(err) - } - sp, err := newXSpanPool(h, 0.6) - if err != nil { - t.Fatal(err) - } - sa := newXStringAllocator(sp) - us := make([]*User, 100000) - for i := 0; i < 100000; i++ { - p, err := sp.Alloc(unsafe.Sizeof(User{})) - if err != nil { - t.Fatal(err) - } - user := (*User)(p) - user.Age = i - user.Name = rand.Int() - strPtr, err := sa.From(fmt.Sprintf("chaoyang_北京_%d", rand.Int())) - if err != nil { - t.Fatal(err) - } - user.Addr = strPtr - us[i] = user - } - - for i, user := range us { - fmt.Println(i, user) - } -} - -func TestXTreap(t *testing.T) { - valAllocator := newXAllocator(unsafe.Sizeof(treapNode{})) - freeChunks := newXTreap(valAllocator) - for i := 1; i < 1000000; i++ { - err := freeChunks.insert(&xChunk{ - startAddr: uintptr(i), - npages: uintptr(i), - }) - if err != nil { - t.Fatal(err) - } - } - - for i := 0; i < 3; i++ { - for i := 1; i < 1000000; i++ { - node, err := freeChunks.find(uintptr(i)) - if err != nil { - t.Fatal(err) - } - freeChunks.removeNode(node) - node.chunk.startAddr += 1 - node.chunk.npages -= 1 - freeChunks.insert(node.chunk) - } - } -} - -func Test_NewUser(t *testing.T) { - t1 := time.Now() - us := make([]*User, 10000000) - key, val := "key", "val" - for i := 0; i < 10000000; i++ { - user := User{} - user.Addr = key - user.Desc = val - us[i] = &user - } - fmt.Println(time.Now().Sub(t1)) -} - -func Test_FromInAddr(t *testing.T) { - f := &Factory{} - mm, err := f.CreateConcurrentHashMapMemory(0.6, 1) - if err != nil { - t.Fatal(err) - } - key, val := "key", "val" - uSize := unsafe.Sizeof(User{}) - t1 := time.Now() - for i := 0; i < 10000000; i++ { - entryPtr, err := mm.Alloc(uSize + uintptr(len(key)) + uintptr(len(val))) - if err != nil { - t.Fatal(err) - } - _ = (*User)(entryPtr) - /* - keyValPtrs, err := mm.FromInAddr(uintptr(entryPtr)+uSize, key, val) - if err != nil { - t.Fatal(err) - } - user.Addr = *(keyValPtrs[0]) - user.Desc = *(keyValPtrs[1]) - */ - /* - user.Addr = key - user.Desc = val - */ - } - fmt.Println(time.Now().Sub(t1)) -} - -func TestMm_Alloc(t *testing.T) { - f := &Factory{} - mm, err := f.CreateMemory(0.6) - if err != nil { - panic(err) - } - uSize := unsafe.Sizeof(User{}) - t1 := time.Now() - us := make([]*User, 10000000) - var wait sync.WaitGroup - wait.Add(10) - for i := 0; i < 10; i++ { - go func() { - defer wait.Done() - for i := 0; i < 1000000; i++ { - entryPtr, err := mm.Alloc(uSize) - if err != nil { - panic(err) - } - user := (*User)(entryPtr) - iii := strconv.Itoa(i) - a, err := mm.From(iii) - if err != nil { - t.Error(err) - } - f1(user, a, a) - us[i] = user - } - }() - } - wait.Wait() - fmt.Println(time.Now().Sub(t1), len(us)) -} - -func f1(user *User, key, val string) { - user.Addr = key - user.Desc = val -} - -func TestMm_GoAlloc(t *testing.T) { - t1 := time.Now() - us := make([]*User, 10000000) - var wait sync.WaitGroup - wait.Add(10) - for i := 0; i < 10; i++ { - go func() { - defer wait.Done() - for i := 0; i < 1000000; i++ { - user := &User{} - iii := strconv.Itoa(i) - key, val := iii, iii - f1(user, key, val) - us[i] = user - } - }() - } - - fmt.Println(time.Now().Sub(t1)) -} - -func Test_String(t *testing.T) { - us := make([]string, 10000000) - i := []byte(cast.ToString("n")) - - for n := 0; n < 8000000; n++ { - j := string(i) - us[n] = j - } -} - -func TestMm_Free(t *testing.T) { - h, err := newXHeap() - if err != nil { - t.Fatal(err) - } - sp, err := newXSpanPool(h, 0.6) - if err != nil { - t.Fatal(err) - } - sp.debug = true - var us []unsafe.Pointer - size := unsafe.Sizeof(User{}) - for i := 0; i < 1000; i++ { - if i%85 == 0 && i > 0 { - //panic清空前85个 - for j := 0; j < 85; j++ { - if err := sp.Free(uintptr(us[j])); err != nil { - t.Fatal(err) - } - } - } - p, err := sp.Alloc(size) - if err != nil { - t.Fatal(err) - } - user := (*User)(p) - user.Age = i - user.Name = rand.Int() - us = append(us, p) - } -} - -func TestMm_Free2(t *testing.T) { - h, err := newXHeap() - if err != nil { - t.Fatal(err) - } - sp, err := newXSpanPool(h, 0.6) - if err != nil { - t.Fatal(err) - } - sp.debug = true - var us []unsafe.Pointer - size := unsafe.Sizeof(User{}) - for i := 0; i < 20000; i++ { - if i == 12000 { - //删除前600个偶数对象 - for j := 0; j < 6000; j += 2 { - if err := sp.Free(uintptr(us[j])); err != nil { - t.Fatal(err) - } - us[j] = nil - } - } - p, err := sp.Alloc(size) - if err != nil { - t.Fatal(err) - } - user := (*User)(p) - user.Age = i - user.Name = rand.Int() - us = append(us, p) - } - - for i, pointer := range us { - if pointer == nil { - continue - } - if sss := (*User)(pointer); sss.Age != i { - t.Fatalf("%+v\n", (*User)(pointer)) - } - } -} - -func TestMm2(t *testing.T) { - f := Factory{} - mm, err := f.CreateMemory(0.6) - if err != nil { - t.Fatal(err) - } - - uSize := unsafe.Sizeof(User{}) - //t1 := time.Now() - us := make(chan uintptr, 1000000) - var wait sync.WaitGroup - wait.Add(10) - for i := 0; i < 10; i++ { - go func(i int) { - defer wait.Done() - for j := 0; j < 100000; j++ { - age := i*100000 + j - entryPtr, err := mm.Alloc(uSize) - if err != nil { - t.Fatal(err) - } - user := (*User)(entryPtr) - user.Age = age - us <- uintptr(entryPtr) - } - }(i) - } - wait.Wait() - - wait = sync.WaitGroup{} - wait.Add(10) - for i := 0; i < 10; i++ { - go func() { - defer wait.Done() - l: - for { - select { - case uPtr := <-us: - user := (*User)(unsafe.Pointer(uPtr)) - if user.Age%2 == 0 { - continue - } - if err := mm.Free(uPtr); err != nil { - t.Fatal(err) - } - case <-time.After(time.Second * 30): - break l - } - } - }() - } - wait.Wait() - - wait = sync.WaitGroup{} - wait.Add(10) - us3 := make(chan uintptr, 1000000) - for i := 0; i < 10; i++ { - go func(i int) { - defer wait.Done() - for j := 0; j < 100000; j++ { - age := i*100000 + j - entryPtr, err := mm.Alloc(uSize) - if err != nil { - t.Fatal(err) - } - user := (*User)(entryPtr) - user.Age = age - us3 <- uintptr(entryPtr) - } - }(i) - } - wait.Wait() - fmt.Println("分配完成") - -l2: - for { - select { - case uPtr := <-us3: - user := (*User)(unsafe.Pointer(uPtr)) - if user.Age == 0 { - t.Error(user) - } - case <-time.After(time.Second * 10): - break l2 - } - } -} - -func TestRawAlloc2(t *testing.T) { - f := Factory{} - mm, err := f.CreateMemory(0.6) - if err != nil { - t.Fatal(err) - } - p, err := mm.RawAlloc(536871918/_PageSize + 1) - if err != nil { - t.Fatal(err) - } - fmt.Println(p.StartAddr, p.Npages) -} - -func Init() { - // 略 - runtime.GOMAXPROCS(6) // 限制 CPU 使用数,避免过载 - runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪 - runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪 - - go func() { - // 启动一个 http server,注意 pprof 相关的 handler 已经自动注册过了 - if err := http.ListenAndServe(":6060", nil); err != nil { - log.Fatal(err) - } - os.Exit(0) - }() - <-time.After(time.Second * 10) -} - -func TestAlloc_Benchmark(t *testing.T) { - // Init() - f := &Factory{} - mm, err := f.CreateMemory(0.75) - if err != nil { - t.Fatal(err) - } - var wait sync.WaitGroup - wait.Add(10) - now := time.Now() - for j := 0; j < 10; j++ { - go func(z int) { - defer wait.Done() - for i := 0; i < 1000000; i++ { - n := rand.Intn(60) + 1 - if _, err := mm.Alloc(uintptr(n)); err != nil { - t.Error(err) - } - } - }(j) - } - wait.Wait() - fmt.Println(time.Now().Sub(now), 10*1000000/1000000, "百万") //300w ops -}