Skip to content
vic

fereidani/arena

Golang Arena Memory Allocator

fereidani/arena.json
{
"createdAt": "2025-07-03T09:18:07Z",
"defaultBranch": "main",
"description": "Golang Arena Memory Allocator",
"fullName": "fereidani/arena",
"homepage": null,
"language": "Go",
"name": "arena",
"pushedAt": "2025-07-03T09:19:11Z",
"stargazersCount": 4,
"topics": [],
"updatedAt": "2025-10-07T23:45:13Z",
"url": "https://github.com/fereidani/arena"
}

A high-performance Go library providing arena-based memory allocation for any type. This library significantly reduces garbage collection pressure and allocation costs by pre-allocating memory in chunks and reusing it efficiently. It can improve performance 3 to 5x for use cases that require allocation of many small objects like binary tree or linked list.

  • Generic Support: Works with any Go type using generics
  • Concurrent Safe: Thread-safe Arena for multi-goroutine scenarios
  • Single-threaded Optimized: LocalArena for better performance in single-goroutine use cases
  • Lock-free Algorithm: Uses atomic operations without mutexes for optimal performance
  • Reduced GC Pressure: Pre-allocates memory in large chunks instead of individual allocations, minimizing garbage collection overhead
  • Zero-copy Design: Returns pointers to pre-allocated memory, reusing memory within chunks to minimize heap pressure
  • Enhanced Performance: Avoids frequent small allocations that cost more GC cycles and improves CPU cache locality by allocating related objects contiguously in memory

Trade-off: If a single object from an arena chunk remains alive, the entire chunk will be kept in memory until that reference is released. This is the cost of the performance that you gain. A wiseman once said with great power comes great irresponsibility.

Terminal window
go get github.com/fereidani/arena

For multi-goroutine scenarios:

package main
import (
"fmt"
"sync"
"github.com/fereidani/arena"
)
type Node struct {
Value int
Left *Node
Right *Node
}
func buildTree(a *arena.Arena[Node], depth int) *Node {
if depth > 25 {
return nil
}
node := a.Get() // *Node
node.Value = depth
node.Left = buildTree(a, depth+1)
node.Right = buildTree(a, depth+1)
return node
}
func main() {
// Create arena with chunk size of 1024
a := arena.NewArena[Node]!(1024)
var wg sync.WaitGroup
// Launch multiple goroutines to build left and right branches concurrently
wg.Add(2)
root := a.Get()
root.Value = 1
go func() {
defer wg.Done()
root.Left = buildTree(a, 2)
}()
go func() {
defer wg.Done()
root.Right = buildTree(a, 2)
}()
wg.Wait()
fmt.Println(root.Value)
}

For single-goroutine scenarios (better performance):

package main
import "github.com/fereidani/arena"
type Node struct {
Value int
Left *Node
Right *Node
}
func buildTree(a *arena.LocalArena[Node], depth int) *Node {
if depth > 25 {
return nil
}
node := a.Get() // *Node
node.Value = depth
node.Left = buildTree(a, depth + 1)
node.Right = buildTree(a, depth + 1)
return node
}
func main() {
// Create local arena with chunk size of 500
a := arena.NewLocalArena[Node]!(1024)
// Build a simple binary tree
root := buildTree(a, 1)
}

Thread-safe arena allocator.

  • NewArena[T]!(chunkSize int) *Arena[T]: Creates a new concurrent arena
  • Get() *T: Returns a pointer to a new allocated item

Single-threaded arena allocator with better performance.

  • NewLocalArena[T]!(chunkSize int) *LocalArena[T]: Creates a new local arena
  • Get() *T: Returns a pointer to a new allocated item
  • Chunks are allocated with the specified size (default: 1024)
  • When a chunk is exhausted, a new chunk of the same size is allocated
  • Memory is not automatically freed - chunks remain allocated until all references are released
  • Choose chunk sizes based on your allocation patterns and memory constraints

MIT License