Skip to content

Commit

Permalink
feat: allocator POC
Browse files Browse the repository at this point in the history
  • Loading branch information
joetifa2003 committed Jul 29, 2024
1 parent bf51e14 commit 11a94c7
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 62 deletions.
108 changes: 108 additions & 0 deletions allocator/arena.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package allocator

import (
"os"
"unsafe"
)

var pageSize = os.Getpagesize() * 1

var chunkSize = int(unsafe.Sizeof(Chunk{}))

type Chunk struct {
data unsafe.Pointer
allocated int
size int
ptrCount int
next *Chunk
prev *Chunk
}

type ArenaAllocator struct {
tail *Chunk
allocator Allocator
}

var arenaAllocatorSize = int(unsafe.Sizeof(ArenaAllocator{}))

func NewArena(allocator Allocator) Allocator {
a := (*ArenaAllocator)(allocator.Alloc(arenaAllocatorSize))
a.allocator = allocator

return a
}

func (a *ArenaAllocator) newChunk(n int) *Chunk {
ch := (*Chunk)(a.allocator.Alloc(chunkSize))
nPages := (n / pageSize) + 1
ch.size = nPages * pageSize
ch.data = a.allocator.Alloc(nPages * pageSize)

return ch
}

type Meta struct {
chunk *Chunk
size int
}

var metaSize = int(unsafe.Sizeof(Meta{}))

func (a *ArenaAllocator) Alloc(n int) unsafe.Pointer {
if a.tail == nil {
a.tail = a.newChunk(n)
}

if a.tail.allocated+n+metaSize > a.tail.size {
ch := a.newChunk(n + metaSize)
ch.prev = a.tail
a.tail.next = ch
a.tail = ch
}
meta := (*Meta)(unsafe.Pointer(uintptr(a.tail.data) + uintptr(a.tail.allocated)))
meta.chunk = a.tail
meta.size = n
ptr := unsafe.Pointer(uintptr(unsafe.Pointer(meta)) + uintptr(metaSize))
a.tail.allocated += n + metaSize
a.tail.ptrCount++

return ptr
}

func (a *ArenaAllocator) freeChunk(ch *Chunk) {
if ch.prev != nil {
ch.prev.next = ch.next
}
if ch.next != nil {
ch.next.prev = ch.prev
}

if ch == a.tail {
a.tail = ch.prev
}

a.allocator.Free(ch.data)
a.allocator.Free(unsafe.Pointer(ch))
}

func (a *ArenaAllocator) Free(ptr unsafe.Pointer) {
meta := (*Meta)(unsafe.Pointer(uintptr(ptr) - uintptr(metaSize)))
meta.chunk.ptrCount--
}

func (a *ArenaAllocator) Destroy() {
for ch := a.tail; ch != nil; ch = ch.next {
a.freeChunk(ch)
}
}

func (a *ArenaAllocator) Realloc(ptr unsafe.Pointer, size int) unsafe.Pointer {
newPtr := a.Alloc(size)

oldMeta := (*Meta)(unsafe.Pointer(uintptr(ptr) - uintptr(metaSize)))
newPtrData := unsafe.Slice((*byte)(newPtr), size)
oldPtrData := unsafe.Slice((*byte)(ptr), oldMeta.size)
copy(newPtrData, oldPtrData)

return newPtr
}
4 changes: 4 additions & 0 deletions allocator/callocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ func (c CAllocator) Realloc(ptr unsafe.Pointer, size int) unsafe.Pointer {
}

func (c CAllocator) Destroy() {}

func NewC() Allocator {
return CAllocator{}
}
38 changes: 5 additions & 33 deletions malloc/malloc.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,23 @@
package malloc

import (
"fmt"
"runtime"
"unsafe"

"github.com/ebitengine/purego"
)

var calloc func(n int, size int) unsafe.Pointer
var realloc func(ptr unsafe.Pointer, size int) unsafe.Pointer
var free func(ptr unsafe.Pointer)

func getSystemLibrary() string {
switch runtime.GOOS {
case "darwin":
return "/usr/lib/libSystem.B.dylib"
case "linux":
return "libc.so.6"
case "windows":
return "ucrtbase.dll"
default:
panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
}
}

func init() {
libc, err := openLibrary(getSystemLibrary())
if err != nil {
panic(err)
}
purego.RegisterLibFunc(&calloc, libc, "calloc")
purego.RegisterLibFunc(&realloc, libc, "realloc")
purego.RegisterLibFunc(&free, libc, "free")
}
//#include <stdlib.h>
import "C"

// CMalloc raw binding to c calloc(1, size)
func Malloc(size int) unsafe.Pointer {
return calloc(1, size)
return C.calloc(1, C.size_t(size))
}

// CMalloc raw binding to c free
func Free(ptr unsafe.Pointer) {
free(ptr)
C.free(ptr)
}

// CMalloc raw binding to c realloc
func Realloc(ptr unsafe.Pointer, size int) unsafe.Pointer {
return realloc(ptr, size)
return C.realloc(ptr, C.size_t(size))
}
9 changes: 0 additions & 9 deletions malloc/malloc_unix.go

This file was deleted.

10 changes: 0 additions & 10 deletions malloc/malloc_windows.go

This file was deleted.

20 changes: 20 additions & 0 deletions mm.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ func getSize[T any]() int {
// Alloc allocates T and returns a pointer to it.
func Alloc[T any](ctx context.Context) *T {
allocator := allocator.GetAllocator(ctx)
return AllocAllocator[T](allocator)
}

func AllocAllocator[T any](allocator allocator.Allocator) *T {
ptr := allocator.Alloc(getSize[T]())
return (*T)(unsafe.Pointer(ptr))
}
Expand All @@ -23,6 +27,10 @@ func Alloc[T any](ctx context.Context) *T {
// CAUTION: be careful not to double free, and prefer using defer to deallocate
func Free[T any](ctx context.Context, ptr *T) {
allocator := allocator.GetAllocator(ctx)
FreeAllocator[T](allocator, ptr)
}

func FreeAllocator[T any](allocator allocator.Allocator, ptr *T) {
allocator.Free(unsafe.Pointer(ptr))
}

Expand All @@ -31,6 +39,10 @@ func Free[T any](ctx context.Context, ptr *T) {
// arithmetic with slice indexing
func AllocMany[T any](ctx context.Context, n int) []T {
allocator := allocator.GetAllocator(ctx)
return AllocManyAllocator[T](allocator, n)
}

func AllocManyAllocator[T any](allocator allocator.Allocator, n int) []T {
ptr := allocator.Alloc(getSize[T]() * n)
return unsafe.Slice(
(*T)(ptr),
Expand All @@ -42,12 +54,20 @@ func AllocMany[T any](ctx context.Context, n int) []T {
// CAUTION: be careful not to double free, and prefer using defer to deallocate
func FreeMany[T any](ctx context.Context, slice []T) {
allocator := allocator.GetAllocator(ctx)
FreeManyAllocator[T](allocator, slice)
}

func FreeManyAllocator[T any](allocator allocator.Allocator, slice []T) {
allocator.Free(unsafe.Pointer(&slice[0]))
}

// Reallocate reallocates memory allocated with AllocMany and doesn't change underling data
func Reallocate[T any](ctx context.Context, slice []T, newN int) []T {
allocator := allocator.GetAllocator(ctx)
return ReallocateAllocator[T](allocator, slice, newN)
}

func ReallocateAllocator[T any](allocator allocator.Allocator, slice []T, newN int) []T {
ptr := allocator.Realloc(unsafe.Pointer(&slice[0]), getSize[T]()*newN)
return unsafe.Slice(
(*T)(ptr),
Expand Down
Loading

0 comments on commit 11a94c7

Please sign in to comment.