blas

BLAS bindings for MoonBit (Accelerate/OpenBLAS)

blas
linear-algebra
matrix
native
moon add mizchi/blas@0.1.3
Download zip
Author
Version
0.1.3
License
MIT
Last updated
21 hours ago
Downloads
2K
README

#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]

#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 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
  • 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 type FloatBuffer Int64

Opaque handle to C-side float buffer

#
FloatBuffer::inner

#deprecated("Use `struct T(A)` to declare a newtype and use `.0` access the underlying type instead.")
fn FloatBuffer::inner(self : FloatBuffer) -> Int64
Convert newtype to its underlying type, automatically derived.

#
IntBuffer

pub type IntBuffer Int64

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

#
IntBuffer::inner

#deprecated("Use `struct T(A)` to declare a newtype and use `.0` access the underlying type instead.")
fn IntBuffer::inner(self : IntBuffer) -> Int64
Convert newtype to its underlying type, automatically derived.

#
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)

#
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