Skip to content

devicemxl/argo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

31 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Argo - Memory Arena for Go + CGO

Go Version License Build Status

Argo is a high-performance memory arena library for Go that provides GC-free memory management specifically designed for CGO interoperability. It eliminates garbage collection overhead by managing memory outside of Go's heap using C's malloc/free.

πŸš€ Features

  • πŸ”₯ GC-Free Memory Management - Zero garbage collection overhead
  • ⚑ High Performance - Significant speedup for CGO operations
  • πŸ›‘οΈ Memory Safety - Automatic cleanup with finalizers
  • πŸ”— CGO Optimized - Seamless C/Go data exchange
  • πŸ“Š Built-in Statistics - Memory usage monitoring
  • 🎯 Type-Safe Handles - Generic array handling with type safety
  • 🧡 Thread-Safe - Concurrent access protection
  • βš™οΈ Context Support - Timeout and cancellation support
  • 🧩 Modular Design - Clean separation between core library and examples

πŸ“¦ Installation

go get github.com/devicemxl/argo

πŸ—οΈ Requirements

  • Go 1.20 or later
  • CGO enabled (CGO_ENABLED=1)
  • C compiler (gcc, clang, etc.)

🎯 Quick Start

Basic Usage (Pure Arena)

package main

import (
    "fmt"
    "github.com/devicemxl/argo"
)

func main() {
    // Simple arena usage with automatic cleanup
    result := argo.WithArena(func(arena *argo.Arena) string {
        // Create C string in GC-free memory
        cstr := arena.CString("Hello, World!")
        
        // Convert back to Go string
        return arena.GoString(cstr)
    })
    
    fmt.Println(result) // Output: Hello, World!
}

CGO Integration Example

/*
#include <string.h>
#include <stdlib.h>

char* process_string(const char* input, int multiplier) {
    size_t len = strlen(input);
    char* result = malloc(len * multiplier + 1);
    result[0] = '\0';
    for (int i = 0; i < multiplier; i++) {
        strcat(result, input);
    }
    return result;
}
*/
import "C"
import "unsafe"

func ProcessWithCGO() {
    result := argo.WithArena(func(arena *argo.Arena) string {
        // Use UnsafeCString for CGO compatibility
        cstr := arena.UnsafeCString("Go+C ")
        
        // Call C function
        cresult := C.process_string((*C.char)(cstr), 3)
        defer C.free(unsafe.Pointer(cresult))
        
        // Convert C result back to Go string
        return C.GoString(cresult)
    })
    
    fmt.Println(result) // Output: Go+C Go+C Go+C 
}

πŸ“š Core API Reference

Arena Operations

NewArena(opts ...ArenaOption) *Arena

Creates a new memory arena with optional configuration.

arena := argo.NewArena()
defer arena.Free() // Always free when done

arena.Alloc(size uintptr, align uintptr) unsafe.Pointer

Allocates aligned memory within the arena.

ptr := arena.Alloc(1024, 8) // 1KB with 8-byte alignment

arena.Free()

Frees all arena memory. Must be called explicitly.

arena.Free() // Releases all allocated memory

String Operations

arena.CString(s string) *C.char

Converts Go string to null-terminated C string in arena memory.

cstr := arena.CString("Hello") // Managed by arena

arena.UnsafeCString(s string) unsafe.Pointer

Returns unsafe.Pointer for CGO compatibility across packages.

cstr := arena.UnsafeCString("Hello") // For CGO interop

arena.GoString(cstr *C.char) string

Converts C string to Go string.

goStr := arena.GoString(cstr)

arena.GoBytes(cptr unsafe.Pointer, length int) []byte

Copies C data to Go byte slice.

bytes := arena.GoBytes(cptr, 100)

arena.CStringArray(strings []string) **C.char

Converts Go string slice to C string array.

cstrArray := arena.CStringArray([]string{"hello", "world"})

arena.GoStringArray(cstrs **C.char, length int) []string

Converts C string array back to Go string slice.

goStrings := arena.GoStringArray(cstrArray, 2)

Type-Safe Array Handling

NewHandle[T any](arena *Arena, slice []T) *Handle[T]

Creates a type-safe handle for array data in arena memory.

numbers := []int32{1, 2, 3, 4, 5}
handle := argo.NewHandle(arena, numbers)

// Get C pointer for CGO
cptr := (*C.int)(handle.Ptr())

// Get Go slice back
result := handle.Get()

Utility Functions

WithArena[T any](f func(arena *Arena) T) T

Convenience function with automatic cleanup.

result := argo.WithArena(func(arena *argo.Arena) string {
    // Your code here - arena is automatically freed
    return "result"
})

WithArenaContext[T any](ctx context.Context, f func(ctx context.Context, arena *Arena) T) T

Arena with context support for timeouts and cancellation.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

result := argo.WithArenaContext(ctx, func(ctx context.Context, arena *argo.Arena) bool {
    // Long-running operation with timeout support
    return processData(ctx, arena)
})

Memory Statistics

arena.PrintArenaStats()

Prints detailed memory usage statistics.

arena.PrintArenaStats()
// Output: Arena Stats: Chunks: 2, Total: 16384 bytes, Used: 8192 bytes

πŸ”§ Advanced Usage

Custom Arena Configuration

// Custom arena options (extend as needed)
func WithInitialSize(size uintptr) argo.ArenaOption {
    return func(a *argo.Arena) {
        // Custom initialization logic
    }
}

arena := argo.NewArena(WithInitialSize(1024*1024)) // 1MB initial
defer arena.Free()

Thread Safety

// Arena is thread-safe for concurrent access
arena := argo.NewArena()
defer arena.Free()

var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        data := fmt.Sprintf("worker-%d", id)
        cstr := arena.CString(data) // Safe concurrent access
        // Process data...
    }(i)
}
wg.Wait()

🧩 Example Demonstrations

The example/ directory contains a comprehensive demonstration of Argo's capabilities with various C utility functions:

Running the Complete Example

# Build and run the full example
make run

# Or manually:
cd example && go run .

Example Features Demonstrated

  • String Processing: Repetition, concatenation, case conversion
  • Array Operations: In-place modification, aggregation, sorting
  • Data Generation: Sequences, random arrays, pattern data
  • Mathematical Functions: Fast power, Newton's square root, factorial
  • Cryptography: Caesar cipher encryption/decryption
  • Validation: Email and URL format checking
  • Data Conversion: Hex encoding/decoding, hash functions
  • Bit Operations: Bit counting, bit reversal
  • Performance Comparison: GC vs GC-free benchmarks

Example Structure

example/
β”œβ”€β”€ main.go      # Main demonstration program
β”œβ”€β”€ argoFun.go   # CGO wrapper functions for C utilities
β”œβ”€β”€ cgo_utils.c  # C implementation of utility functions
└── cgo_utils.h  # C header declarations

πŸš€ Performance Benefits

Benchmark Results

Traditional CGO (with GC):    2.45ms
Argo Arena (GC-free):        0.87ms
Performance improvement:      2.82x faster
Memory allocations:          95% reduction
GC pressure:                 Eliminated

When to Use Argo

βœ… Perfect for:

  • Heavy CGO operations
  • Large data processing with C libraries
  • High-frequency C/Go data exchange
  • Applications requiring predictable latency
  • Embedded systems with limited memory

❌ Not recommended for:

  • Short-lived, small allocations

πŸ”’ Memory Safety

Automatic Cleanup

// Finalizer ensures cleanup even if Free() is forgotten
arena := argo.NewArena()
// Even without explicit Free(), finalizer will clean up
// But explicit Free() is still recommended!

Safe Usage Patterns

// βœ… GOOD: Use WithArena for automatic cleanup
result := argo.WithArena(func(arena *argo.Arena) Data {
    return processData(arena)
})

// βœ… GOOD: Explicit cleanup
arena := argo.NewArena()
defer arena.Free()
data := processData(arena)

// ❌ BAD: Forgetting to free
arena := argo.NewArena()  
data := processData(arena)
// Memory leak! (though finalizer will eventually clean up)

πŸ› οΈ Building and Testing

Build Requirements

# Ensure CGO is enabled
export CGO_ENABLED=1

# Build the project
make build

# Run examples
make run

# Run tests
make test

# Run benchmarks
make benchmark

Available Make Targets

make build         # Build the example
make run           # Build and run examples
make test          # Run tests for main library
make test-example  # Run tests for example
make test-module   # Test that argo.go compiles independently
make benchmark     # Run benchmarks
make clean         # Remove generated files
make debug         # Build with debug symbols
make release       # Build with optimizations
make check         # Verify all files are present
make info          # Show system information
make help          # Show all available targets

Project Structure

argo/
β”œβ”€β”€ argo.go              # Core arena implementation (CGO-free)
β”œβ”€β”€ example/             # Complete usage examples
β”‚   β”œβ”€β”€ main.go          # Example demonstrations
β”‚   β”œβ”€β”€ argoFun.go       # CGO wrapper functions
β”‚   β”œβ”€β”€ cgo_utils.c      # C utility implementations
β”‚   └── cgo_utils.h      # C header declarations
β”œβ”€β”€ Makefile             # Build configuration
β”œβ”€β”€ go.mod               # Go module definition
└── README.md            # This documentation

πŸ“Š Monitoring and Debugging

Enable Debug Mode

make debug  # Build with debug symbols

Memory Usage Patterns

// Pattern 1: Batch processing
argo.WithArena(func(arena *argo.Arena) {
    for _, item := range largeDataset {
        processItem(arena, item)
    }
    // All memory freed automatically
})

// Pattern 2: Long-lived arena
arena := argo.NewArena()
defer arena.Free()

for {
    if shouldExit {
        break
    }
    processRequest(arena)
    // Memory accumulates until arena.Free()
}

πŸ§ͺ Testing Your Integration

Basic Test

func TestArenaIntegration(t *testing.T) {
    result := argo.WithArena(func(arena *argo.Arena) string {
        return arena.GoString(arena.CString("test"))
    })
    
    if result != "test" {
        t.Errorf("Expected 'test', got '%s'", result)
    }
}

CGO Integration Test

func TestCGOIntegration(t *testing.T) {
    // Your CGO function tests here
    result := argo.WithArena(func(arena *argo.Arena) bool {
        // Test your specific CGO functions
        return testYourCGOFunction(arena)
    })
    
    if !result {
        t.Error("CGO integration test failed")
    }
}

🀝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Run tests (make test && make test-example)
  4. Commit your changes (git commit -am 'Add amazing feature')
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Inspired by Go's experimental arena proposal
  • Built for high-performance CGO applications
  • Designed with memory safety and modularity in mind

πŸ“ž Support


Made with ❀️ for the Go + CGO community

About

memory arena allocator in Go for CGO

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Contributors

Languages