cache_padded

    Cache-padded data structures for MoonBit to reduce false sharing

    cache
    padding
    performance
    concurrency
    cache-line
    optimization
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    last year
    Downloads
    32

    #moonbit-cache-padded

    ⚠ BUT: This library currently requires runtime overhead due to MoonBit's limitations in compile-time alignment and struct layout stability.


    #Overview

    False sharing occurs when multiple threads access different variables that happen to reside on the same cache line, causing unnecessary cache invalidation and performance degradation. This library provides cache-padded wrappers that ensure each value occupies its own cache line.

    #Usage

    // Create cache-padded integer
    let padded_counter = CachePaddedInt::new(42)

    // Access the value
    let value = padded_counter.get() // Returns 42

    // Update the value
    padded_counter.set(100)
    let new_value = padded_counter.get() // Returns 100

    // Update using a transformation function
    padded_counter.update(fn(x) { x * 2 })

    // Clean up (important!)
    padded_counter.destroy()

    #Why Not Zero-Cost Abstraction?

    Unlike Rust's CachePadded which achieves true zero-cost abstraction through compile-time alignment, this MoonBit implementation currently requires runtime overhead. Here's why:

    #Current MoonBit Limitations

    1. No Compile-Time Alignment Attributes: MoonBit currently lacks compile-time alignment directives like Rust's repr(align(N)) or C++'s alignas. There's no way to specify cache line alignment at the type level.

    2. Unstable Struct Layout: According to MoonBit's FFI documentation, "The layout of struct/enum with payload is currently unstable." This means:
      • The compiler may reorder struct fields
      • Padding size and position are unpredictable
      • Memory layout cannot be guaranteed

    3. FFI Dependency: To achieve proper cache alignment, we must use C FFI, which introduces:
      • Runtime heap allocation (malloc)
      • Function call overhead across language boundaries
      • Manual memory management requirements
      • Cross-language performance penalties

    #Comparison with Rust's Zero-Cost Implementation

    FeatureRust CachePaddedThis MoonBit Implementation
    Memory AllocationStack allocationHeap allocation (malloc)
    Alignment MethodCompile-time repr(align)Runtime pointer calculation
    Access OverheadZero-cost (Deref trait)FFI function calls
    Memory ManagementAutomatic cleanupManual destroy() required
    Space OverheadExact padding calculationExtra cache line allocation

    #Rust's Zero-Cost Example

    // Rust achieves true zero-cost abstraction #[repr(align(64))] pub struct CachePadded<T> { value: T, } // Stack allocated, compile-time aligned, zero runtime overhead let padded = CachePadded::new(42); // No malloc! let value = *padded; // Direct memory access, no function call!

    #Future Possibilities

    MoonBit may eventually support zero-cost cache padding through:
    • Compiler intrinsics: Built-in alignment functions
    • Attribute system: Compile-time alignment directives like #[repr(align = 64)]
    • Generic specialization: Compiler-optimized specialization for specific types

    #When to Use This Library

    Despite the runtime overhead, this implementation provides significant value when:
    • High contention scenarios: Performance gains (5-20x) outweigh allocation costs
    • Long-lived objects: Allocation cost amortized over object lifetime
    • Critical sections: Preventing false sharing is more important than allocation overhead

    The trade-off is justified in multi-threaded scenarios where false sharing elimination provides substantial performance benefits that exceed the FFI and allocation costs.

    #Memory Layout

    The library ensures that each padded value occupies its own cache line, preventing false sharing. On most modern systems, cache lines are 64 bytes.

    // Without padding: two variables might share a cache line
    let a = 1
    let b = 2 // Might be on same cache line as 'a'

    // With padding: each variable gets its own cache line
    let padded_a = CachePaddedInt::new(1)
    let padded_b = CachePaddedInt::new(2) // Guaranteed on different cache line

    #Verification of Cache Line Alignment

    The implementation guarantees cache line alignment through:

    1. 64-byte allocation: Each CachePaddedInt occupies exactly one cache line (64 bytes)
    2. Pointer alignment: Memory addresses are aligned to cache line boundaries using bitwise operations
    3. Padding calculation: Automatic padding ensures no two instances share a cache line

    You can verify the cache line size your system uses:
    let cache_size = get_cache_line_size() // Returns 64 on most modern systems

    #Development

    # Check code moon check --target native # Run tests moon test --target native # Format code moon fmt

    #License

    MIT License

    Source Files