MMatrix is my personal C matrix library playground for macOS and Linux, supporting both dense and sparse matrix formats with SIMD and BLAS acceleration.
sm: Single-precision (float) dense matrixdm: Double-precision (double) dense matrixdms: Double-precision sparse matrix (COO format)
All modules support:
- Random matrix creation using PCG (permuted congruential generator)
- Elementwise and matrix operations
- Console-based printing
- I/O for:
- MATLAB
.matfiles (viamatioandhdf5) - Matrix Market files
- MATLAB
- [macOS] Optimized with Apple Accelerate (vDSP, BLAS, LAPACK)
- [macOS] Optional use of Metal Performance Shaders (MPS)
- SIMD acceleration with OpenMP and [macOS] ARM NEON intrinsics
- PCG-based random number generation for reproducibility and parallelism
MPS break-even (measured): On Apple Silicon, MPS begins to outperform
Accelerate for GEMM only around roughly
| Scenario | Recommended | Reason |
|---|---|---|
| Dense or moderately sparse matrix, N ≤ 10k | sm.h / dm.h |
BLAS/Accelerate kernels are hard to beat; memory fits easily on a laptop |
| Sparse matrix (density < 1%), N up to ~20k | dms.h |
COO + CSparse keeps overhead low; native SpGEMM with OpenMP scales well |
| Very large sparse matrices (N > 50k, nnz > 1M) | SuiteSparse:GraphBLAS | Parallel CSC kernels, semiring algebra, and optimized memory management pay off at scale |
Rule of thumb: For graphs with fewer than ~20k nodes, dms.h combined with
CSparse provides an excellent balance of simplicity and performance. For dense
or high-density graphs under 10k nodes, prefer sm.h (float) or dm.h
(double) — the BLAS-accelerated dense path is faster and the memory footprint is
still manageable. For truly large-scale sparse problems (100k+ nodes, millions
of non-zeros), consider SuiteSparse:GraphBLAS,
which is available as a Bazel dependency in this project (@graphblas).
- Overview and API style:
docs/Overview.md - Performance notes:
docs/Performance.md - Grafana benchmark workflow:
docs/Grafana.md - Float dense API (
sm):docs/api/sm.md - Double dense API (
dm):docs/api/dm.md - Sparse COO API (
dms):docs/api/dms.md
Ensure required dependencies are installed via Homebrew (Linux too):
brew install openblas libomp suitesparse matio llvmThen build using Bazel on macOS:
bazel build //app/matrixOn Linux the default host config automatically uses Homebrew LLVM plus OpenBLAS:
bazel build //app/matrixbazel build //app/matrix now picks the default backend automatically:
- macOS: OpenMP + Accelerate
- Linux: OpenMP + OpenBLAS
You can still override the backend explicitly when needed:
-
Force Accelerate:
bazel build //app/matrix --config=accelerate
-
Force OpenBLAS:
bazel build //app/matrix --config=openblas
-
Force plain OpenMP fallback:
bazel build //app/matrix --config=openmp_only
MPS (Metal Performance Shaders) is still built in automatically for Accelerate builds on macOS.
To activate MPS at runtime, call sm_set_backend(SM_BACKEND_MPS) in your code.
You can install the compiled library and headers into a custom directory using the Bazel installer target:
bazel run //:matrix_installer -- /your/installation/pathi.e.
bazel run //:matrix_installer -- $(PWD)/lib/matrixThe installer supports two modes:
standard(default): installslibmatrix.aplus dependency libs and headers.bundle: creates and installs one combined static archivelibmatrix_full.a(fat static lib) and installs headers.
Examples:
# Default mode
bazel run //:matrix_installer -- --mode=standard $(PWD)/lib/matrix
# Bundle mode (single static archive)
bazel run //:matrix_installer -- --mode=bundle $(PWD)/lib/matrix_bundleTo also create symbolic links into system-wide directories (e.g., /usr/local/include, /usr/local/lib), you can enable system integration in BUILD - File:
installer(
name = "matrix_installer",
data = [
"//app/matrix", # the target to be installed
"//app/matrix:matrix_header", # collected in a Bazel filegroup with all public headers
"//:LICENSE.txt", # if available
"@libomp//:install_files",
"@log",
"@matio",
"@openblas//:install_files",
"@pcg",
"@suitesparse//:install_files",
"@zlib",
],
system_integration = False, # set "True" symlinks into /usr/local/lib and /usr/local/include
)This will:
- Copy the compiled static library (
libmatrix.a) and dependencies intolib/ - Install public headers into
include/ - Optionally create symlinks for easy access from standard system locations
Unit tests are written using Unity:
bazel test //...Tests are located in the app/matrix/tests/ folder. You can add new test files and they will be discovered automatically by Bazel.
FloatMatrix *A = sm_create_random(128, 128);
FloatMatrix *B = sm_transpose(A);
sm_destroy(A);
sm_destroy(B);- macOS (ARM64 preferred, supports Apple Silicon M1/M2/M3 ...)
- Linux (x86 64bit, tested on Ubuntu + Homebrew)
- Clang/LLVM toolchain with OpenMP support required
This project depends on:
- OpenBLAS
- libomp
- SuiteSparse
- matio
- LLVM (via Homebrew)
All dependencies are pulled via Bazel modules or expected to be installed via Homebrew.
This project is primarily licensed under the MIT License (see LICENSE). It includes files from the Bazel project, licensed under the Apache License 2.0 (see tools/install/*, ).