sofieBLAS is an abstract C++ (header-only) interface for BLAS operations targeting heterogeneous architectures. It currently supports only ALPAKA buffers and the GEMM operation, acting as a thin, efficient wrapper over existing BLAS libraries such as OpenBLAS, MKL, BLIS, Apple Accelerate, cuBLASLt, and hipBLASLt - allowing the actual backend to be selected through template-based dispatching using traits.
We plan to extend support to more BLAS routines and buffer types in future releases.
- Unified Interface: Common C++ API over multiple BLAS backends.
- Heterogeneous Support:
- CPU: OpenBLAS, MKL, BLIS, Apple Accelerate (any CBLAS-compatible library).
- GPU: NVIDIA (cuBLASLt) and AMD (hipBLASLt).
- Template-Based Dispatching: Backend selection via traits at compile-time, keyed on the Alpaka accelerator tag (e.g.
alpaka::TagCpuSerial,alpaka::TagGpuCudaRt,alpaka::TagGpuHipRt). - Header-Only: Lightweight, easy to integrate- no separate compilation required.
- Minimal Dependency Overhead: Only depends on the backend BLAS libraries of choice.
- One File Per Backend: Each vendor library (CPU or GPU) lives in its own header under
include/sofieBLAS/backends/<cpu|cuda|hip>/, selected at compile time via a preprocessor macro (ALPAKA_ACC_GPU_CUDA_ENABLED,ALPAKA_ACC_GPU_HIP_ENABLED,SOFIEBLAS_USE_OPENBLAS,SOFIEBLAS_USE_MKL,SOFIEBLAS_USE_BLIS,SOFIEBLAS_USE_ACCELERATE) so only the code for the library you actually build against gets compiled.
Backend selection happens entirely at compile time, in two steps:
- Compiler-flag macros decide the backend implementations. A macro (or macro pair) must be defined to compile in a given backend's header - see the table below. For CPU, if
ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLEDis defined but none of theSOFIEBLAS_USE_*macros are, sofieBLAS defaults to OpenBLAS; the GPU backends have no such default (each is tied 1:1 to itsALPAKA_ACC_GPU_*_ENABLEDmacro, so there's nothing to default between). - The Alpaka tag you instantiate
sofieBLAS<Tag>with decides which compiled-in backend a given call site actually uses, via thetraits::sofieBLAS<Tag>specialization (e.g.sofieBLAS<alpaka::TagGpuHipRt>resolves to the hipBLASLt backend). There is no runtime dispatch - if the macro for that tag's backend wasn't defined, the code simply won't compile.
| Accelerator | Macro(s) | Alpaka tag |
|---|---|---|
| CPU (OpenBLAS) | ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLED + SOFIEBLAS_USE_OPENBLAS |
alpaka::TagCpuSerial (or other TagCpu*) |
| CPU (Intel MKL) | ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLED + SOFIEBLAS_USE_MKL |
alpaka::TagCpuSerial (or other TagCpu*) |
| CPU (BLIS) | ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLED + SOFIEBLAS_USE_BLIS |
alpaka::TagCpuSerial (or other TagCpu*) |
| CPU (Apple Accelerate) | ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLED + SOFIEBLAS_USE_ACCELERATE |
alpaka::TagCpuSerial (or other TagCpu*) |
| NVIDIA GPU (cuBLASLt) | ALPAKA_ACC_GPU_CUDA_ENABLED |
alpaka::TagGpuCudaRt |
| AMD GPU (hipBLASLt) | ALPAKA_ACC_GPU_HIP_ENABLED |
alpaka::TagGpuHipRt |
sofieBLAS/sofieBLAS.hpp includes the matching backend header(s) for you based on these macros; nothing else needs to change in your source beyond picking the right tag.
sofieBLAS itself is header-only (add_subdirectory/find_package(sofieBLAS) gives you an INTERFACE target with no build step). Tests and benchmarks are opt-in via CMake options and auto-detect whichever backends are available on the machine for each backend target (test_cpu/test_cuda/test_hip, bench_cpu/bench_cuda/bench_hip) and is skipped if its dependency isn't found:
cmake -B build -S . -DSOFIEBLAS_BUILD_TESTS=ON -DSOFIEBLAS_BUILD_BENCHMARKS=ON
cmake --build build -j"$(nproc)"
# Run the correctness tests (matmul/gemm/gemmrelu/gemmgelu vs. a reference
# implementation, per available backend)
ctest --test-dir build --output-on-failure
# Run the GEMM throughput benchmark
./build/benchmark/bench_cuda -w 5 -n 20 --sizes 256,512,1024,2048,4096CPU_BLAS_LIB (OpenBLAS/MKL/BLIS/Accelerate) and CUDA_BASE/ROCM_BASE/ONEAPI_BASE/BLIS_BASE are available as -D cache variables to point at non-default install locations; alpaka is picked up via find_package(alpaka) if already installed, otherwise fetched automatically. See tests/CMakeLists.txt and benchmark/CMakeLists.txt for the target definitions, and cmake/SofieBLASBackends.cmake for the shared backend-detection logic.
#include "sofieBLAS/sofieBLAS.hpp"
#include <alpaka/alpaka.hpp>
#include <iostream>
int main() {
constexpr uint32_t size = 4;
// Create Alpaka CPU device and blocking queue
alpaka::PlatformCpu platform;
auto device = alpaka::getDevByIdx(platform, 0u);
alpaka::Queue<alpaka::DevCpu, alpaka::Blocking> queue{device};
// Allocate and initialize matrices A, B, C on CPU
auto A = alpaka::allocBuf<float, uint32_t>(device, size * size);
auto B = alpaka::allocBuf<float, uint32_t>(device, size * size);
auto C = alpaka::allocBuf<float, uint32_t>(device, size * size);
// (Initialize A and B here...)
// Create sofieBLAS instance for the CPU backend selected via
// SOFIEBLAS_USE_OPENBLAS / SOFIEBLAS_USE_MKL / SOFIEBLAS_USE_BLIS / SOFIEBLAS_USE_ACCELERATE
sofieBLAS<alpaka::TagCpuSerial> blas(queue);
// C = alpha * op(A) * op(B) + beta * C (leading dimensions inferred from m, n, k)
blas.matmul('N', 'N', size, size, size, 1.0f, A, B, 0.0f, C);
alpaka::wait(queue);
std::cout << "GEMM completed on CPU backend.\n";
return 0;
}Switching to a GPU is the same code shape, just a different Alpaka tag, queue/device type, and build-time macro. For example, on AMD (built with -DALPAKA_ACC_GPU_HIP_ENABLED):
alpaka::PlatformHipRt platform;
auto device = alpaka::getDevByIdx(platform, 0u);
alpaka::Queue<alpaka::DevHipRt, alpaka::NonBlocking> queue{device};
sofieBLAS<alpaka::TagGpuHipRt> blas(queue);
blas.matmul('N', 'N', size, size, size, 1.0f, dA, dB, 0.0f, dC);The GPU backends (BlasCuda, BlasHip) additionally expose gemmrelu/gemmgelu (fused bias + activation via cuBLASLt/hipBLASLt epilogues), gemmStridedBatched, and addLayoutConfig (used to pre-register cuBLASLt/hipBLASLt matrix layouts for a given shape before the first matmul/gemm call on that shape).
Contributions, feature suggestions, and issue reports are welcome! Feel free to open a PR or an issue.