OpenMathLib/OpenBLAS
> The default open-source optimized BLAS: hand-tuned assembly GEMM kernels for ~100 CPU targets, carrying the GotoBLAS lineage that most of scientific Python, Julia, and R silently runs on.
GitHub repo · Official website · License: BSD-3-Clause
Overview
OpenBLAS is an optimized implementation of BLAS (Basic Linear Algebra Subprograms) with a bundled LAPACK, forked in 2011 by Zhang Xianyi's group at the Chinese Academy of Sciences from GotoBLAS2 1.13, after Kazushige Goto stopped developing the original at TACC[^1]. The performance model is Goto's: block matrix operands for each level of the cache hierarchy, pack them into contiguous buffers, and run an inner micro-kernel written in hand-scheduled assembly per microarchitecture[^2]. Fifteen years later the project's main activity is still exactly that — adding and tuning kernels for new CPUs (recent releases cover ARM SVE/SME, RISC-V RVV 1.0, LoongArch, POWER10, Zen 5).
Its importance exceeds its ~7.5k stars by an order of magnitude: OpenBLAS is the BLAS behind NumPy and SciPy PyPI wheels, ships inside Julia, and is the default libblas alternative on most Linux distributions. When "numpy is using all my cores" or "my container burns CPU doing nothing," OpenBLAS's thread pool is frequently the actual subject.
The defining tension: OpenBLAS competes with vendor libraries (Intel oneMKL, Apple Accelerate, AMD's BLIS-based AOCL) that have paid teams per architecture, using a mostly volunteer effort held together for years primarily by one maintainer, Martin Kroeker[^3]. It stays competitive on large-matrix Level-3 BLAS across an unmatched breadth of hardware, but small-matrix performance, threading heuristics, and thread-safety edges have historically lagged the vendors. The repo moved from xianyi/OpenBLAS to the OpenMathLib org in 2023; the master branch is far out of date and develop is the real default branch[^4].
Getting Started
# package managers
sudo apt install libopenblas-dev # Debian/Ubuntu
brew install openblas # macOS
# from source — auto-detects CPU; see TargetList.txt for TARGET values
git clone https://github.com/OpenMathLib/OpenBLAS && cd OpenBLAS
make -j # or: make TARGET=HASWELL DYNAMIC_ARCH=1
make install PREFIX=/opt/OpenBLAS # repeat the same make options here
// dgemm.c — C := alpha*A*B + beta*C via the CBLAS interface
#include <cblas.h>
#include <stdio.h>
int main(void) {
double A[4] = {1, 2, 3, 4}, B[4] = {5, 6, 7, 8}, C[4] = {0};
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
2, 2, 2, 1.0, A, 2, B, 2, 0.0, C, 2);
printf("%f %f\n%f %f\n", C[0], C[1], C[2], C[3]);
return 0;
}
// gcc dgemm.c -lopenblas && ./a.out
Architecture / How It Works
The codebase is layered, and the layering is the whole story[^2]:
interface/— thin BLAS/CBLAS/LAPACK entry points that validate arguments and dispatch. Fortran (dgemm_), C (cblas_dgemm), and LAPACKE surfaces all funnel to the same drivers.driver/— the architecture-independent blocking and threading logic. Level-3 routines partition matrices by per-CPU tuned block sizes (GEMM_P,GEMM_Q,GEMM_Rinparam.h), pack panels into aligned contiguous buffers, and split work across the thread pool.kernel/— the payload: per-architecture micro-kernels, largely hand-written assembly (kernel/x86_64/dgemm_kernel_4x8_haswell.Sand hundreds of siblings).getarchprobes the build host and selects the kernel set and parameters at compile time. Some x86 kernels were originally auto-generated by the AUGEM template system[^5].lapack/+ bundled netlib LAPACK — OpenBLAS ships reference LAPACK (currently 3.12) and overrides a handful of hot routines (LU/Cholesky factorizations) with parallelized versions. If no Fortran compiler is present, an f2c-translated C LAPACK is used instead.
Two build decisions shape everything downstream. DYNAMIC_ARCH=1 compiles all kernel sets into one fat binary with runtime CPUID dispatch — this is what distros and NumPy wheels ship, at the cost of binary size and a "generic enough" parameter choice per family. Threading model is chosen at build time between plain pthreads (OpenBLAS's own spinning thread pool), OpenMP (USE_OPENMP=1), or serial — and the three variants behave differently under composition with application threads (see below). The maximum thread count (NUM_THREADS) is also baked in at build time, and per-thread GEMM buffers are reserved against it.
Production Notes
The thread pool is the number-one operational footgun. The pthread build keeps worker threads spin-waiting after a call completes (to reduce latency for the next one), which reads as mysterious CPU burn in profilers. Worse, OpenBLAS defaults to using every core it sees: combine that with Python multiprocessing (N processes × N BLAS threads) and throughput collapses from oversubscription. The standard fixes are OPENBLAS_NUM_THREADS=1 in worker processes, or threadpoolctl to clamp pools at runtime[^6]. In containers, OpenBLAS may size its pool from the host's core count rather than the cgroup quota — set the env var explicitly.
Composition rules differ by build. The OpenMP build cooperates with an application's own OpenMP regions (BLAS calls inside a parallel region run single-threaded); the pthread build does not know about your threads at all. Calling the pthread build concurrently from many application threads was historically racy; it is now supported, but a single-threaded (USE_THREAD=0) build needs USE_LOCKING=1 to be safe when called from multithreaded code[^7]. Fork safety relies on pthread_atfork handlers and has been a recurring bug source with Python multiprocessing.
Build-time caps bite later. A binary built with NUM_THREADS=64 will never use more than 64 cores, and distro packages have shipped with surprisingly low caps. Memory for GEMM buffers scales with the cap, which matters on many-core machines.
ILP64 is a parallel universe. Arrays beyond 2^31 elements need the 64-bit-integer interface (INTERFACE64=1). Loading LP64 and ILP64 BLAS into one process (e.g., R plus a NumPy wheel) causes silent symbol collisions unless the ILP64 build was made with a symbol suffix (SYMBOLSUFFIX=64_), which is why distro ILP64 packages exist and why mixing BLAS providers in one process is a classic source of wrong-results bugs.
Reproducibility caveats. Results legitimately differ across thread counts, kernel targets, and versions (different summation orders). Bitwise-identical runs require pinning threads to 1 and the target kernel — and even then, not across versions. CONSISTENT_FPCSR=1 exists to sync FP control state across threads.
Performance positioning. For large-matrix Level-3 routines OpenBLAS is generally within striking distance of oneMKL and clearly ahead of reference netlib BLAS (often 10x+). Weak spots by reputation: very small matrices (call overhead and no batched API worth the name), some Level-1/2 routines, and threading thresholds that spin up cores for problems too small to benefit.
When to Use / When Not
Use when:
- You need a permissively licensed, redistributable optimized BLAS/LAPACK — the default choice for shipping numerical software.
- You target heterogeneous or non-x86 hardware: ARM64, POWER, s390x, RISC-V, LoongArch coverage is unmatched among open BLAS implementations.
- You are already using it implicitly (NumPy/SciPy wheels, Julia) and mostly need to operate it well: thread caps, ILP64 discipline.
Avoid when:
- You are on Intel hardware and can accept a proprietary dependency — oneMKL is usually as fast or faster, especially on small matrices, and adds FFT/sparse/batched routines OpenBLAS lacks.
- You are on Apple Silicon — Accelerate uses the AMX/SME units and beats OpenBLAS's NEON kernels on most sizes.
- Your workload is dominated by tiny matrices (robotics, per-element 3x3/4x4 work) — a template library like Eigen inlines and wins.
- You need GPU BLAS — this is strictly a CPU library; use cuBLAS/rocBLAS.
Alternatives
- flame/blis — modern, cleaner kernel-framework redesign of the Goto approach; basis of AMD's AOCL. Use instead when you want a structured framework for adding kernels, or best AMD Zen tuning.
- Reference-LAPACK/lapack — the netlib reference (bundled inside OpenBLAS). Use directly when you need a correctness baseline, not speed.
- Intel oneMKL (proprietary, free binary) — use on Intel x86 when licensing allows; broader routine coverage and better small-matrix performance.
- Apple Accelerate (proprietary, OS-bundled) — use on macOS/iOS; AMX-accelerated and zero install.
- libeigen/eigen — header-only C++ templates; use when matrices are small/fixed-size or you want expression fusion instead of a BLAS ABI.
History
| Version | Date | Notes | |---------|------|-------| | fork | 2011-01 | Repo created; fork of GotoBLAS2 1.13 BSD after upstream development ended[^1]. | | 0.2.8 | 2013-08 | 0.2.x era: Sandy Bridge AVX kernels, ARM port begins[^4]. | | 0.2.20 | 2017-07 | Final 0.2.x release. | | 0.3.0 | 2018-05 | 0.3.x line: thread-safety rework, ongoing LAPACK version bumps[^4]. | | 0.3.13 | 2020-12 | Cooper Lake BFLOAT16 SBGEMM; Apple Silicon support era begins. | | 0.3.21 | 2022-08 | AVX-512 SGEMM/DGEMM improvements, Zen tuning, RISC-V targets grow. | | 0.3.24 | 2023-09 | First release in the OpenMathLib org after the move from xianyi[^4]. | | 0.3.28 | 2024-08 | RISC-V RVV 1.0 targets, SVE kernel expansion. | | 0.3.31 | 2026-01 | ARMv9 SME target work, Neoverse V3 support[^4]. | | 0.3.34 | 2026-07 | Latest release at time of writing[^4]. |
References
[^1]: Kazushige Goto and Robert van de Geijn, "Anatomy of High-Performance Matrix Multiplication," ACM TOMS 34(3), 2008 — the GotoBLAS design OpenBLAS inherits. https://doi.org/10.1145/1356052.1356053 [^2]: OpenBLAS documentation, "Developer manual / How it works." http://www.openmathlib.org/OpenBLAS/docs/ [^3]: OpenBLAS contributors graph — commit activity concentrated in martin-frbg since ~2016. https://github.com/OpenMathLib/OpenBLAS/graphs/contributors [^4]: OpenBLAS releases (dates per GitHub releases page). https://github.com/OpenMathLib/OpenBLAS/releases [^5]: Qian Wang, Xianyi Zhang, Yunquan Zhang, Qing Yi, "AUGEM: Automatically Generate High Performance Dense Linear Algebra Kernels on x86 CPUs," SC '13. https://doi.org/10.1145/2503210.2503219 [^6]: threadpoolctl — runtime control of BLAS/OpenMP thread pools, the standard mitigation in the Python ecosystem. https://github.com/joblib/threadpoolctl [^7]: OpenBLAS FAQ, "multi-threaded" and thread-safety entries. http://www.openmathlib.org/OpenBLAS/docs/faq/
Tags
c, assembly, fortran, blas, lapack, linear-algebra, numerical-computing, hpc, simd, scientific-computing, native-library