Sign in

    blas

    BLAS bindings for MoonBit (Accelerate/OpenBLAS)

    blas
    linear-algebra
    matrix
    native
    Download zip
    Author
    Version
    0.1.3
    License
    MIT
    Last updated
    2 months ago
    Downloads
    2K

    #mizchi/blas

    BLAS (Basic Linear Algebra Subprograms) bindings for MoonBit.

    • macOS: Apple Accelerate.framework
    • Linux: OpenBLAS

    #Installation

    moon add mizchi/blas

    #Usage

    // Matrix multiplication: C = A @ B
    let a = [1.0, 2.0, 3.0, 4.0] // 2x2 matrix
    let b = [5.0, 6.0, 7.0, 8.0] // 2x2 matrix
    let c = [0.0, 0.0, 0.0, 0.0] // result
    @blas.sgemm(a, b, c, 2, 2, 2)
    // c = [19.0, 22.0, 43.0, 50.0]

    For repeated multiplication with persistent C-side buffers, one FFI call can execute multiple SGEMMs:

    let a_buf = @blas.alloc_floats(m * k)
    let b_buf = @blas.alloc_floats(k * n)
    let c_buf = @blas.alloc_floats(m * n)
    @blas.copy_to_buffer(a_buf, a)
    @blas.copy_to_buffer(b_buf, b)
    @blas.sgemm_direct_batch(a_buf, b_buf, c_buf, m, n, k, iterations=16)

    #High-performance MLP forward pass

    // Create C-side buffers (zero-copy after initialization)
    let bufs = @blas.mlp_buffers_create(batch_size, input_dim, hidden_dim, output_dim)
    @blas.mlp_buffers_init(bufs, input, weight1, bias1, weight2, bias2)

    // Forward pass (all computation in C memory)
    @blas.mlp_forward_fused(bufs)

    // Get output
    let output = Array::make(batch_size * output_dim, 0.0)
    @blas.mlp_buffers_get_output(bufs, output)

    // Cleanup
    @blas.mlp_buffers_free(bufs)

    #Batch training (forward + backward + update)

    let bufs = @blas.mlp_train_buffers_create(batch_size, input_dim, hidden_dim, output_dim)
    @blas.mlp_train_buffers_init_weights(bufs, weight1, bias1, weight2, bias2)

    // Single call does forward, backward, and parameter update
    let (loss_sum, correct_count) = @blas.mlp_train_step(bufs, batch_input, batch_labels, learning_rate)

    @blas.mlp_train_buffers_free(bufs)

    #Quick Commands

    just # check + test just bench # run benchmark just bench-square # compare Array, direct, and batch16 square SGEMM just docker-build # build Linux container just docker-test # test on Linux just docker-bench # benchmark on Linux

    #Platform Support

    Important: You must add the appropriate link flags to your package's moon.pkg:

    #macOS

    options( link: { "native": { "cc-link-flags": "-framework Accelerate" } }, )

    Uses Apple Accelerate.framework (no additional setup required).

    #Linux

    options( link: { "native": { "cc-link-flags": "-lopenblas -llapack -lm" } }, )

    Requires OpenBLAS and LAPACK:

    # Ubuntu/Debian sudo apt-get install libopenblas-dev liblapack-dev # Or use Docker just docker-build just docker-test

    #Header Resolution

    The C stub uses preprocessor directives for cross-platform support:

    #ifdef __APPLE__ #include <Accelerate/Accelerate.h> // macOS system framework #else #include <cblas.h> // OpenBLAS #endif

    PlatformHeaderLibraryNotes
    macOS<Accelerate/Accelerate.h>-framework AccelerateSystem default
    Linux<cblas.h>-lopenblas -lmRequires libopenblas-dev

    Both provide the standard CBLAS API (cblas_sgemm, cblas_sgemv, etc.).

    #Benchmark

    MNIST 2-layer MLP (784→128→10), batch size 128:

    BackendTime/epochvs Pure MoonBit
    Pure MoonBit CPU130s1x
    BLAS batch0.7s186x

    #API

    #Low-level BLAS operations

    • sgemm(a, b, c, m, n, k) - Matrix multiply: C = A @ B
    • sgemm_direct_batch(a, b, c, m, n, k, iterations) - Repeated SGEMM in one FFI call
    • sgemv(a, x, y, m, n) - Matrix-vector multiply: y = A @ x
    • saxpy(alpha, x, y) - Vector add: y = alpha * x + y
    • sdot(x, y) - Dot product
    • snrm2(x) - L2 norm

    #High-level MLP operations

    • mlp_buffers_create/init/free - C-side buffer management
    • mlp_forward_fused - Fused forward pass (layer1+layer2)
    • mlp_train_buffers_create/init_weights/get_weights/free - Training buffer management
    • mlp_train_step - Complete training step (forward + backward + update)

    #License

    MIT

    FloatBuffer

    pub struct FloatBuffer(Int64)

    Opaque handle to C-side float buffer

    IntBuffer

    pub struct IntBuffer(Int64)

    Opaque handle to C-side int buffer (for labels)

    MlpBuffers

    pub struct MlpBuffers {
    input : FloatBuffer
    weight1 : FloatBuffer
    bias1 : FloatBuffer
    hidden : FloatBuffer
    weight2 : FloatBuffer
    bias2 : FloatBuffer
    output : FloatBuffer
    batch : Int
    input_dim : Int
    hidden_dim : Int
    output_dim : Int
    }

    MlpTrainBuffers

    pub struct MlpTrainBuffers {
    weight1 : FloatBuffer
    bias1 : FloatBuffer
    weight2 : FloatBuffer
    bias2 : FloatBuffer
    input : FloatBuffer
    hidden : FloatBuffer
    output : FloatBuffer
    probs : FloatBuffer
    grad_w1 : FloatBuffer
    grad_b1 : FloatBuffer
    grad_w2 : FloatBuffer
    grad_b2 : FloatBuffer
    delta2 : FloatBuffer
    delta1 : FloatBuffer
    labels : IntBuffer
    result : FloatBuffer
    batch : Int
    input_dim : Int
    hidden_dim : Int
    output_dim : Int
    }

    Training buffers for batch BLAS training

    alloc_floats

    fn alloc_floats(count : Int) -> FloatBuffer

    Allocate a C-side float buffer

    alloc_ints

    fn alloc_ints(count : Int) -> IntBuffer

    Allocate a C-side int buffer

    copy_from_buffer

    fn copy_from_buffer(arr : Array[Float], buf : FloatBuffer) -> Unit

    Copy from C buffer to MoonBit Array[Float]

    copy_to_buffer

    fn copy_to_buffer(buf : FloatBuffer, arr : Array[Float]) -> Unit

    Copy from MoonBit Array[Float] to C buffer

    free_floats

    fn free_floats(buf : FloatBuffer) -> Unit

    Free a C-side float buffer

    free_ints

    fn free_ints(buf : IntBuffer) -> Unit

    Free a C-side int buffer

    get_float

    fn get_float(buf : FloatBuffer, idx : Int) -> Float

    Get float at index from C buffer

    get_int

    fn get_int(buf : IntBuffer, idx : Int) -> Int

    Get int at index from C buffer

    layer1_fused

    fn layer1_fused(input : FloatBuffer, weight : FloatBuffer, bias : FloatBuffer, output : FloatBuffer, batch : Int, in_dim : Int, out_dim : Int) -> Unit

    Fused layer1: output = ReLU(input @ weight + bias) input: batch x in_dim, weight: in_dim x out_dim, bias: out_dim, output: batch x out_dim

    layer2_fused

    fn layer2_fused(input : FloatBuffer, weight : FloatBuffer, bias : FloatBuffer, output : FloatBuffer, batch : Int, in_dim : Int, out_dim : Int) -> Unit

    Fused layer2: output = input @ weight + bias (no activation) input: batch x in_dim, weight: in_dim x out_dim, bias: out_dim, output: batch x out_dim

    mlp_buffers_create

    fn mlp_buffers_create(batch : Int, input_dim : Int, hidden_dim : Int, output_dim : Int) -> MlpBuffers

    Create MLP buffers in C memory for zero-copy forward

    mlp_buffers_free

    fn mlp_buffers_free(bufs : MlpBuffers) -> Unit

    Free MLP buffers

    mlp_buffers_get_output

    fn mlp_buffers_get_output(bufs : MlpBuffers, out : Array[Float]) -> Unit

    Copy output from C buffers back to MoonBit array

    mlp_buffers_init

    fn mlp_buffers_init(bufs : MlpBuffers, input_data : Array[Float], weight1_data : Array[Float], bias1_data : Array[Float], weight2_data : Array[Float], bias2_data : Array[Float]) -> Unit

    Initialize MLP buffers with data (called once before benchmarking)

    mlp_forward_fused

    fn mlp_forward_fused(bufs : MlpBuffers) -> Unit

    Run MLP forward pass using C-side buffers (no data copy)

    mlp_train_buffers_create

    fn mlp_train_buffers_create(batch : Int, input_dim : Int, hidden_dim : Int, output_dim : Int) -> MlpTrainBuffers

    Create training buffers

    mlp_train_buffers_free

    fn mlp_train_buffers_free(bufs : MlpTrainBuffers) -> Unit

    Free training buffers

    mlp_train_buffers_get_weights

    fn mlp_train_buffers_get_weights(bufs : MlpTrainBuffers, weight1 : Array[Float], bias1 : Array[Float], weight2 : Array[Float], bias2 : Array[Float]) -> Unit

    Copy weights from training buffers back to arrays

    mlp_train_buffers_init_weights

    fn mlp_train_buffers_init_weights(bufs : MlpTrainBuffers, weight1 : Array[Float], bias1 : Array[Float], weight2 : Array[Float], bias2 : Array[Float]) -> Unit

    Initialize weights in training buffers

    mlp_train_step

    fn mlp_train_step(bufs : MlpTrainBuffers, input : Array[Float], labels : Array[Int], lr : Float) -> (Float, Int)

    Run a complete training step: forward + backward + update Returns (loss_sum, correct_count)

    saxpy

    fn saxpy(alpha : Float, x : Array[Float], y : Array[Float]) -> Unit

    Vector add: y = alpha * x + y

    sdot

    fn sdot(x : Array[Float], y : Array[Float]) -> Float

    Dot product: x . y

    set_float

    fn set_float(buf : FloatBuffer, idx : Int, val : Float) -> Unit

    Set float at index in C buffer

    set_int

    fn set_int(buf : IntBuffer, idx : Int, val : Int) -> Unit

    Set int at index in C buffer

    sgemm

    fn sgemm(a : Array[Float], b : Array[Float], c : Array[Float], m : Int, n : Int, k : Int) -> Unit

    Matrix multiply: C = A @ B A: m x k, B: k x n, C: m x n (row-major)

    sgemm_direct

    fn sgemm_direct(a : FloatBuffer, b : FloatBuffer, c : FloatBuffer, m : Int, n : Int, k : Int) -> Unit

    Matrix multiply on C buffers: C = A @ B A: m x k, B: k x n, C: m x n (row-major)

    sgemm_direct_batch

    fn sgemm_direct_batch(a : FloatBuffer, b : FloatBuffer, c : FloatBuffer, m : Int, n : Int, k : Int, iterations~ : Int) -> Unit

    Repeats C = A @ B in one native call while reusing all C-side buffers.

    sgemv

    fn sgemv(a : Array[Float], x : Array[Float], y : Array[Float], m : Int, n : Int) -> Unit

    Matrix-vector multiply: y = A @ x A: m x n, x: n, y: m (row-major)

    sgemv_trans

    fn sgemv_trans(a : Array[Float], x : Array[Float], y : Array[Float], m : Int, n : Int) -> Unit

    Matrix-vector multiply (transposed): y = A^T @ x A: m x n, x: m, y: n (row-major)

    snrm2

    fn snrm2(x : Array[Float]) -> Float

    L2 norm: ||x||_2

    Source Files