Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions batchallocator/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@ func batchAllocatorDestroy(a unsafe.Pointer) {
b.Free(balloc.alloc)
}

parentAllocator := balloc.alloc
balloc.buckets.Free()
allocator.Free(balloc.alloc, balloc)
parentAllocator.Destroy()
}

// Helper function to handle memory alignment for a given pointer
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/edsrzf/mmap-go v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -12,6 +14,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
31 changes: 30 additions & 1 deletion hashmap/hashmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"github.com/joetifa2003/mm-go/allocator"
"github.com/joetifa2003/mm-go/batchallocator"
"github.com/joetifa2003/mm-go/hashmap"
"github.com/joetifa2003/mm-go/mmapallocator"
)

const TIMES = 500
const TIMES = 100

func BenchmarkHashmapGo(b *testing.B) {
for i := 0; i < b.N; i++ {
Expand Down Expand Up @@ -51,6 +52,34 @@ func BenchmarkHashmapBatchAlloc(b *testing.B) {
}
}

func BenchmarkHashmapBatchAllocMMap(b *testing.B) {
for i := 0; i < b.N; i++ {
alloc := batchallocator.New(mmapallocator.NewMMapAllocator())
h := hashmap.New[int, int](alloc)

for i := 0; i < TIMES; i++ {
h.Set(i, i)
}

h.Free()
alloc.Destroy()
}
}

func BenchmarkHashmapMMap(b *testing.B) {
for i := 0; i < b.N; i++ {
alloc := mmapallocator.NewMMapAllocator()
h := hashmap.New[int, int](alloc)

for i := 0; i < TIMES; i++ {
h.Set(i, i)
}

h.Free()
alloc.Destroy()
}
}

func newMap() map[int]int {
return make(map[int]int)
}
6 changes: 6 additions & 0 deletions mm.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ func Zero[T any]() T {
var zeroV T
return zeroV
}

// Helper function to handle memory alignment for a given pointer
func Align(ptr uintptr, alignment uintptr) uintptr {
mask := alignment - 1
return (ptr + mask) &^ mask
}
24 changes: 24 additions & 0 deletions mm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/joetifa2003/mm-go"
"github.com/joetifa2003/mm-go/allocator"
"github.com/joetifa2003/mm-go/batchallocator"
"github.com/joetifa2003/mm-go/mmapallocator"
"github.com/joetifa2003/mm-go/typedarena"
)

Expand Down Expand Up @@ -96,6 +97,16 @@ func BenchmarkLinkedListBatchAllocator(b *testing.B) {
}
}

func BenchmarkLinkedListBatchAllocatorMMap(b *testing.B) {
for _, bucketSize := range []int{100, 200, 500, LINKED_LIST_SIZE} {
b.Run(fmt.Sprintf("bucket size %d", bucketSize), func(b *testing.B) {
for range b.N {
benchLinkedListBatchAllocatorMMap(b, LINKED_LIST_SIZE, bucketSize)
}
})
}
}

func BenchmarkLinkedListTypedArena(b *testing.B) {
for _, chunkSize := range []int{100, 200, 500, LINKED_LIST_SIZE} {
b.Run(fmt.Sprintf("chunk size %d", chunkSize), func(b *testing.B) {
Expand Down Expand Up @@ -158,6 +169,19 @@ func benchLinkedListBatchAllocator(b *testing.B, size int, bucketSize int) {
assertLinkedList(b, list)
}

func benchLinkedListBatchAllocatorMMap(b *testing.B, size int, bucketSize int) {
alloc := batchallocator.New(mmapallocator.NewMMapAllocator(),
batchallocator.WithBucketSize(mm.SizeOf[Node[int]]()*bucketSize),
)
defer alloc.Destroy()

list := allocator.Alloc[LinkedList[int]](alloc)
for i := range size {
linkedListPushAlloc(alloc, list, i)
}
assertLinkedList(b, list)
}

func assertLinkedList(t *testing.B, list *LinkedList[int]) {
if list.head == nil {
t.Fatal("list head is nil")
Expand Down
114 changes: 114 additions & 0 deletions mmapallocator/mmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package mmapallocator

import (
"os"
"unsafe"

"github.com/edsrzf/mmap-go"

"github.com/joetifa2003/mm-go"
"github.com/joetifa2003/mm-go/allocator"
)

const (
sizeClassCount = 9
bigSizeClass = -1
)

var sizeClasses = [sizeClassCount]int{32, 64, 128, 256, 512, 1024, 2048, 4096, bigSizeClass}

func getSizeClass(size int) (int, int) {
for i, sizeClass := range sizeClasses {
if size <= sizeClass {
return i, sizeClass
}
}

return len(sizeClasses) - 1, bigSizeClass
}

var freeLists [sizeClassCount]*block

type block struct {
data unsafe.Pointer
chunk *chunk
next *block
size int
}

type ptrMeta struct {
block *block
}

type chunk struct {
data unsafe.Pointer
offset uintptr
size uintptr
ptrs uintptr
}

var pageSize = os.Getpagesize() * 15

const (
sizeOfPtrMeta = unsafe.Sizeof(ptrMeta{})
sizeOfBlock = unsafe.Sizeof(block{})
sizeOfChunk = unsafe.Sizeof(chunk{})
alignment = unsafe.Alignof(uintptr(0))
)

func NewMMapAllocator() allocator.Allocator {
return allocator.NewAllocator(nil, mmap_alloc, mmap_free, mmap_realloc, mmap_destroy)
}

func mmap_alloc(allocator unsafe.Pointer, size int) unsafe.Pointer {
sizeClassIdx, sizeClass := getSizeClass(int(sizeOfPtrMeta) + size)

b := freeLists[sizeClassIdx]
if b != nil {
ptrMeta := (*ptrMeta)(unsafe.Pointer(uintptr(b.data)))
ptrMeta.block = b
b.chunk.ptrs++
freeLists[sizeClassIdx] = b.next
return unsafe.Pointer(uintptr(unsafe.Pointer(ptrMeta)) + sizeOfPtrMeta)
}

// init size class
totalSize := mm.Align(sizeOfChunk+uintptr(sizeClass)+sizeOfBlock, uintptr(pageSize))
m, err := mmap.MapRegion(nil, int(totalSize), mmap.RDWR, mmap.ANON, 0)
if err != nil {
panic(err)
}

chunk := (*chunk)(unsafe.Pointer(&m[0]))
chunk.data = unsafe.Pointer(unsafe.Pointer(&m[sizeOfChunk]))
chunk.offset = 0
chunk.size = totalSize - sizeOfChunk

nBlocks := chunk.size / (uintptr(sizeClass) + sizeOfBlock)
for i := uintptr(0); i < nBlocks; i++ {
b := (*block)(unsafe.Pointer(uintptr(chunk.data) + uintptr(i*sizeOfBlock)))
b.data = unsafe.Pointer(uintptr(chunk.data) + uintptr(i*(uintptr(sizeClass)+sizeOfBlock)))
b.next = freeLists[sizeClassIdx]
b.size = sizeClass
b.chunk = chunk
freeLists[sizeClassIdx] = b
}

return mmap_alloc(allocator, size)
}

func mmap_free(allocator unsafe.Pointer, ptr unsafe.Pointer) {
ptrMeta := (*ptrMeta)(unsafe.Pointer(uintptr(ptr) - sizeOfPtrMeta))
block := ptrMeta.block
sizeClassIdx, _ := getSizeClass(block.size)
block.chunk.ptrs--
head := freeLists[sizeClassIdx]
block.next = head
freeLists[sizeClassIdx] = block
}

func mmap_realloc(allocator unsafe.Pointer, ptr unsafe.Pointer, size int) unsafe.Pointer {
return nil
}

func mmap_destroy(allocator unsafe.Pointer) {}
23 changes: 23 additions & 0 deletions mmapallocator/mmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package mmapallocator_test

import (
"testing"

"github.com/joetifa2003/mm-go/allocator"
"github.com/joetifa2003/mm-go/mmapallocator"
)

func TestMMapAllocator(t *testing.T) {
alloc := mmapallocator.NewMMapAllocator()

ptr := allocator.Alloc[int](alloc)
*ptr = 1
ptr2 := allocator.Alloc[int](alloc)
*ptr2 = 2
allocator.Free(alloc, ptr2)
ptr3 := allocator.Alloc[int](alloc)
_ = ptr3
allocator.Free(alloc, ptr)
allocator.Free(alloc, ptr3)
alloc.Destroy()
}
Loading