High-Performance Modern C++ Tensor Engine for Automatic SIMD Vectorization of Recurrences via Data Layout Interleaving (DLI).
Write your core numerical algorithm once using standard, readable scalar C++ math. Legolas++ automatically maps and executes it at peak hardware efficiency across SIMD vector execution units, multi-core CPU threads, and dedicated GPUs:
-
Single Natural Scalar Code: Written with standard loops and scalar arithmetic (
$Y_i = A \cdot X_i + B \cdot Y_{i-1}$ ). Zero assembly intrinsics, zero compiler pragmas, zero vendor lock-in. - Legolas++ DLI Engine: Transforms your problem layout in memory via Data Layout Interleaving (DLI), turning impossible loop-carried recurrences into contiguous vector streams.
-
A Multitude of Hardware Targets:
- ⚡ CPU SIMD Vectorized: Full vector width utilization on ARM NEON (4 floats / 128-bit), x86 AVX2 (8 floats / 256-bit), and x86 AVX-512 (16 floats / 512-bit) at 100% hardware line-rate.
- 🚀 Multi-Core Parallel: Built-in, zero-dependency
StaticThreadPoolwith dynamic chunk scheduling scaling across 4 to 128+ CPU cores, with vector SIMD active on every thread. - 🔥 GPU Acceleration: Optional header-only Vulkan Compute backend for Linux and Windows, and native Apple Metal backend for macOS with unified memory.
You need to apply an intrinsically sequential algorithm (recurrence relation, recursive filter, time-stepping scheme, tridiagonal solver $y_n = f(y_{n-1}, x_n)$) to a massive batch of independent problem instances of the same size (thousands of 1D grid lines, dozens of audio tracks, video streams, or neural network channels).
- Standard Multi-Threading (OpenMP / Threads): Parallelizes across CPU cores, but within each core, compilers cannot vectorize across sequential dependencies. Hardware SIMD execution units (AVX2, AVX-512, NEON) sit idle—wasting 75% to 93% of the CPU's theoretical compute capacity.
- Manual SIMD Intrinsics (
_mm256_..., NEON): Attempting to vectorize manually across instances requires writing hundreds of lines of assembly-like intrinsics. The code becomes unreadable, non-portable, and a maintenance nightmare.
- 100% Machine Utilization: Fully utilizes all CPU cores and 100% of SIMD vector register widths simultaneously.
- Natural Scalar Notation: You write your core algorithm once, as a simple sequential loop in standard scalar math.
- Hardware Vectorization by Construction: Through Data Layout Interleaving (DLI), vectorization is structural in memory and guaranteed—no reliance on fragile compiler heuristics.
- Zero-Overhead Portability: Pure header-only C++14 running with peak efficiency across Apple Silicon (NEON), Linux (x86_64 AVX2 / AVX-512), and Windows MSVC.
While multi-threading (multi-core thread scheduling) is a widely available and commoditized feature in modern computing, hardware SIMD vectorization across linear recurrences has historically remained an intractable barrier.
Standard optimizing compilers (Clang, GCC, MSVC, Intel oneAPI) systematically fail to auto-vectorize loops with loop-carried dependencies (e.g. tridiagonal solvers, recursive IIR filters, ADI sweeps).
Legolas++ breaks this recurrence barrier at the CPU register level. Using Data Layout Interleaving (DLI), Legolas++ reorganizes memory across problem instances so that the exact same generic scalar loop maps directly to full-width hardware vector registers (ARM NEON, x86 AVX2 / AVX-512) without writing a single line of intrinsics or inline assembly.
Many essential algorithms in scientific simulation, digital signal processing, edge AI, and deep learning feature strict loop-carried data dependencies where iteration
// Tridiagonal elimination (Thomas forward sweep), recursive IIR filters, Gauss-Seidel:
for (int i = 1; i < N; ++i) {
X[i] = (B[i] - L[i] * X[i-1]) * invD[i]; // Strict recurrence: RAW hazard!
}Because of this sequential dependency chain:
Every optimizing compiler falls back to scalar execution, leaving up to 90% of the CPU's vector compute capacity completely idle.
In real-world applications, engineers rarely solve a single isolated recurrence. Instead, they process ensembles of independent problem instances:
- Scientific Computing & PDEs: Thousands of 1D tridiagonal systems across 2D/3D ADI grids, heat diffusion, or fluid flow.
- Real-Time Audio DSP: Filtering 32, 64, or 128 audio channels concurrently with recursive IIR/Biquad filters.
- Edge AI & Computer Vision: Depthwise Separable Convolutions across channels (MobileNet, ConvNeXt).
Instead of struggling to vectorize sequentially along
Figure: Data Layout Interleaving (DLI) in Action. Elements at step $i$ across $P=4$ independent problem instances are mapped contiguously into physical memory, transforming strided access into single-instruction aligned SIMD streaming.
This computing pattern appears everywhere across high-performance engineering. Legolas++ includes dedicated, self-contained examples and tutorials for each:
| Domain | Intrinsically Sequential Kernel | Batch Dimension (Interleaved) | Live Example & Tutorial |
|---|---|---|---|
| Scientific Computing & PDEs | Tridiagonal Gaussian elimination (Thomas algorithm, ADI sweeps) | Thousands of 1D spatial lines in 2D/3D grids | 📄 Tridiagonal Thomas Tutorial (code) |
| Real-Time Audio Processing | Recursive IIR Biquad filter (sample |
64+ concurrent audio channels / DAW mixer tracks | 🎧 Audio Biquad Showcase (code) |
| Computer Vision & Video | Temporal recursive motion differencing / spatial convolution | 32 concurrent 720p HD live camera streams | 🎥 Video Pipeline Showcase (code) |
| Deep Learning & Edge AI | MobileNet depthwise 2D convolutions | 32–512 feature map channels | ⚡ Depthwise Conv Showcase (code) |
| Quantitative Finance | 1D finite-difference PDE time-stepping (Black-Scholes / Dupire) | Tens of thousands of independent option contracts | 📈 Option Pricing Engine |
Write your numerical solver once using natural scalar notation; Legolas++ auto-vectorizes it in hardware SIMD and scales across CPU cores simultaneously:
#include <iostream>
#include "Legolas/Array/Array.hxx"
#include "Legolas/Array/Map.hxx"
// 1. Write numerical algorithm once using standard scalar math:
struct Scaler {
template <class A2D>
void operator()(int begin, int end, A2D X, A2D Y) const {
using Scalar = typename A2D::RealType;
Scalar factor(2.5f);
for (int j = begin; j < end; ++j) {
for (int i = 0; i < X[j].size(); ++i) {
Y[j][i] = factor * X[j][i]; // Compiles to NEON / AVX2 FMA automatically!
}
}
}
};
int main() {
// 2. Interleave 1024 systems of size 256 with pack size P=4:
using Tensor2D = Legolas::Array<float, 2, 4, 2>;
Tensor2D X(1024, 256), Y(1024, 256);
X.fill(1.0f);
// 3. Parallelize across cores AND vectorize in hardware SIMD simultaneously:
Legolas::parmap(Scaler(), X, Y);
std::cout << "Computed 1024 systems at full hardware vector line-rate!" << std::endl;
return 0;
}High-performance computing is fundamentally an energy efficiency challenge. On mobile platforms, drones, and data centers, unvectorized scalar loops waste energy by leaving SIMD hardware idle while drawing baseline power. Through Data Layout Interleaving (DLI), Legolas++ achieves peak arithmetic density, reducing energy consumption per operation by up to 87%:
| Application | Domain | Scale | Performance (Apple M1 Max) | Speedup | Energy & Power Footprint | Showcase |
|---|---|---|---|---|---|---|
| Video Pipeline (CPU) | Vision & Broadcast | 32 HD 720p streams | 8,442 FPS (7.78 GPix/s) | 5.34× | 3.55 µJ / frame (0.259 GPix/W) | Tutorial |
| Video Pipeline (Metal GPU) | Vision & Broadcast | 32 HD 720p streams | 17,264 FPS (15.91 GPix/s) | 57.1× | 2.43 µJ / frame (0.379 GPix/W) · 87.2% energy saved | Tutorial |
| Audio IIR Biquad | Audio DSP & Acoustics | 64 parallel channels | 5,832 MSamples/sec | 14.7× | 5.14 nJ / sample (< 0.05 W total draw) | Tutorial |
| Depthwise 2D Conv | Edge AI (MobileNet) | 512 channels, |
212.3 GFlops (8 Cores) | 5.83× | 7.08 GFlops/Watt · 82.8% energy saved | Tutorial |
| MultiThomas Solver | Scientific Computing | 262,144 systems, 134M unknowns | 69.40 GFlops (8 Cores) | 33.1× | 2.31 GFlops/Watt (33× more math per Joule) | Tutorial |
Location: examples/VideoPipeline/VideoPipeline.cxx & VideoPipelineMetal.mm
- 3×3 spatial Sobel edge detection fused with quadratic temporal motion differencing across 32 concurrent 720p HD feeds.
- CPU Line-Rate: 8,442.1 FPS (7.78 GPixels/s), 3.79 ms latency per 32-frame batch (5.34× speedup vs scalar).
- Metal GPU Line-Rate: 17,263.9 FPS (15.91 GPixels/s), 1.85 ms latency (57.15× speedup vs scalar, 7.57× vs CPU).
- Energy Impact: Drops energy per HD frame from 19.0 µJ (scalar) down to 2.43 µJ on Metal GPU (87.2% reduction). A single 40 W laptop sustains 287 concurrent 60 FPS feeds without thermal throttling.
Location: examples/AudioBiquad/AudioBiquad.cxx
- 2nd-order Direct Form II recursive IIR filter (
$y[n] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2]$ ). - Temporal recurrence vectorized across 64 audio channels using
$P=4$ NEON SIMD packing. - Result: 5,832 Megasamples/sec (14.66× speedup).
- Energy Impact: Filters 64 channels of studio 192 kHz audio using less than 0.05 W of power (5.14 nJ per sample). Replaces multi-kilowatt dedicated hardware DSP racks with pure C++ software.
Location: examples/DepthwiseConv/DepthwiseConv.cxx
- Core compute primitive of lightweight vision neural networks (MobileNet, ConvNeXt, EfficientNet).
- Evaluates
$3\times3$ spatial filters across 512 channels simultaneously with NEONfmla.4svector instructions. - Result: 212.3 GFlops (5.83× speedup over scalar).
- Energy Impact: Delivers 7.08 GFlops/Watt on CPU with zero PCIe memory copy latency, cutting inference power consumption by 82.8% to extend battery life in robotics and drones.
Location: tst/MultiThomas/MultiThomas.cxx
- 262,144 tridiagonal systems (134.2 million unknowns) for Alternating Direction Implicit (ADI) heat diffusion and Navier-Stokes sweeps.
- Result: 69.40 GFlops (33.05× speedup over scalar).
- Energy Impact: Delivers 2.31 GFlops/Watt on tightly coupled recurrences where optimizing compilers drop to 0.07 GFlops/Watt, achieving 33× higher compute density per Joule.
How close does Legolas++ operate to the physical limits of modern silicon? We model every workload using the Roofline Model ($P_{\text{attainable}} = \min(P_{\text{peak}}, I \times B_{\text{peak}})$):
| Benchmark / Kernel | Arithmetic Intensity ( |
Physical Bottleneck | Hardware Efficiency (Measured) |
|---|---|---|---|
|
MultiThomas ( |
Memory Bandwidth (DRAM) | 81% of GPU VRAM limit / 45% of CPU DRAM | |
| VideoPipeline (32×720p) | Streaming Memory Bus | 79.5% of Apple UMA Peak (191 GB/s) | |
| AudioBiquad (64 Tracks) | Recurrence Dependency / FMA Pipe | 100% of FMA Vector Issue Capacity | |
| DepthwiseConv (128ch) | L2/L3 Cache Bandwidth | 85% of L2/L3 Peak Bandwidth (628 GFlops) | |
| OptionPricing (16k opts) | Compute-Bound (L3 Cache) | 88% of Multi-Core FMA Execution (1.5M opts/s) | |
Reductions (squaredNorm) |
VRAM Streaming | 75% of Physical VRAM Peak (335 GB/s) |
For complete mathematical derivations, memory traffic equations, and vector plots, see the Roofline Performance Model Guide.
| Feature | Standard Compilers (GCC/Clang/MSVC) | Traditional Linear Algebra (BLAS, Armadillo, Blaze) | Frameworks (PyTorch C++ ATen, oneDNN) | Legolas++ |
|---|---|---|---|---|
| Vectorize Recurrences ($X_i = f(X_{i-1})$) | ❌ Fails (falls back to |
❌ Scalar loops ( |
❌ Requires custom handwritten kernels | ✅ Automatic SIMD ( |
| Write Once, Vectorize Everywhere | ❌ Requires manual intrinsics / pragmas | ❌ Separate CPU/GPU/SIMD code paths | ✅ Single generic scalar syntax | |
| Data Layout Interleaving (DLI) | ❌ Manual array-of-structs reshuffling | ❌ Fixed row/column major layouts | ✅ Native in the tensor type system | |
| External Dependencies | None | BLAS / LAPACK | Heavy (LibTorch, MKL, Python) | ✅ Zero (Pure Standard C++14) |
| Build & Integration Model | N/A | Variable | Multi-gigabyte binaries | ✅ 100% Header-Only (INTERFACE)
|
┌─────────────────────────────────────────────────────────────┐
│ Level 1: Multi-Core Scaling │
│ Legolas::parmap(...) via persistent StaticThreadPool │
│ Dynamic chunks claimed from an atomic counter (4/worker) │
│ Hybrid spin-then-sleep idle policy (no hot-path locks) │
└──────────────────────────────┬──────────────────────────────┘
│ (blocked ranges)
┌──────────────────────────────▼──────────────────────────────┐
│ Level 2: Hardware SIMD Vectorization (Core) │
│ Legolas::Array<T, D, P, DP> (Data Layout Interleaving) │
│ Packed views mapped directly to ARM NEON / x86 AVX2/512 │
│ Zero memory permute overhead during computation │
└─────────────────────────────────────────────────────────────┘
Legolas++ is designed with a strict 100% header-only and zero-dependency philosophy. No binary libraries (.a, .so, .dylib, .dll) need to be compiled or linked.
- Standard C++14 compliant compiler:
- Apple Clang
$\ge 12$ - GCC
$\ge 7$ - LLVM Clang
$\ge 8$ - Microsoft Visual C++ (MSVC)
$\ge 2017$
- Apple Clang
- CMake
$\ge 3.5$ - Zero external dependencies required (native standard threads and SIMD wrappers)
Add Legolas++ to your CMakeLists.txt in 4 lines:
include(FetchContent)
FetchContent_Declare(
Legolas
GIT_REPOSITORY https://github.com/LaurentPlagne/Legolas.git
GIT_TAG master
)
FetchContent_MakeAvailable(Legolas)
add_executable(my_solver main.cpp)
target_link_libraries(my_solver PRIVATE Legolas)Alternatively, if using Git submodules or a local clone:
add_subdirectory(path/to/Legolas)
target_link_libraries(my_solver PRIVATE Legolas)Or simply add the include directory to your compiler invocation:
c++ -O3 -std=c++14 -I/path/to/Legolas -I/path/to/Legolas/Legolas/include main.cpp -o my_solver# Configure Release build
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Compile tests and benchmarks
cmake --build build -j
# Run the automated test suite (100% passing)
ctest --test-dir build --output-on-failureStarting with version 2.1, Legolas++ includes an optional, fully self-contained Vulkan compute backend designed for Linux and Windows platforms (macOS utilizes the native Apple Metal backend).
- Zero Link-Time Dependencies: The Vulkan loader is dynamically opened at runtime via
dlopen(Linux) orLoadLibrary(Windows) throughvkGetInstanceProcAddr(VK_NO_PROTOTYPES). The backend links only${CMAKE_DL_LIBS}. No Vulkan SDK or import libraries are needed at link time. - In-Tree Versioned SPIR-V: Compute shaders are precompiled and versioned directly inside the repository (
Legolas/Vulkan/spv/*.hxx) asconst uint32_t[]arrays. Neitherglslcnor any shader compiler SDK is required to build or run. - Transparent CPU Fallback: If no Vulkan-capable GPU or driver is present on the system,
Legolas::Vulkan::Context::available()evaluates tofalse, and execution falls back cleanly to the CPU path. - GPU Mapping of Data Layout Interleaving (DLI):
- A GPU thread naturally replaces a CPU SIMD lane.
- Transposing the problem ensemble to an
[step][system](Structure of Arrays / SoA) layout guarantees 100% memory coalescing across GPU warps/wavefronts. - For tridiagonal recurrences, each GPU thread processes 4 systems simultaneously via
vec4, reducing address calculations, exposing instruction-level parallelism (ILP), and boosting compute throughput by 4.8× over scalar thread mappings.
Workloads measured against single-core scalar, single-core AVX2 DLI, and a 12-thread CPU baseline (AMD Ryzen 5 3600):
| Workload | GPU Kernel Time | Speedup vs Scalar | Speedup vs 12-Thread CPU | Verdict |
|---|---|---|---|---|
| VideoPipeline (32×720p Sobel + Temporal) | 0.98 ms | 57.3× | 18.0× | 🚀 Peak line-rate (32,659 FPS) |
| DepthwiseConv (128ch, 3×3 MobileNet) | 0.044 ms | 12.8× | 4.9× | 🚀 628 GFlops sustained |
|
MultiThomas ( |
13.8 ms | 58.0× | 9.1× | 🚀 127 GFlops sustained |
| AudioBiquad (64ch × 960k samples, blocked scan) | 9.9 ms | 15.7× | 2.8× | 🚀 6,176 MSamples/s |
Reductions (squaredNorm, dot, 16.7M floats) |
0.20–0.32 ms | — | 39–62× | 🚀 Bandwidth saturated |
⚠️ Key Takeaway: Resident Data vs. Discrete PCIe Transfers
On discrete GPUs, single-shot isolated executions are bounded by the PCIe bus transfer rate (~4.5 GB/s). The GPU backend delivers massive speedups when data stays resident in VRAM (iterative PDE solvers, multi-pass rendering/video pipelines, or audio streaming), or on Unified Memory Architectures (UMA) such as Apple Silicon or integrated APUs where zero-copy host-visible buffers are used (Context::isUnifiedMemory()).
Enabling the Vulkan backend is opt-in via CMake:
# 1. Configure with Vulkan enabled (requires vulkan headers on Linux/Windows)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DLEGOLAS_ENABLE_VULKAN=ON
# 2. Build the benchmark suite and tests
cmake --build build -j
# 3. Run all tests (including Vulkan verification)
ctest --test-dir build --output-on-failure
# 4. Run the comprehensive Vulkan benchmark suite:
./build/examples/VulkanBench # Full suite
./build/examples/VulkanBench --skip-heavy # Fast smoke testFor complete implementation notes, architectural details, and regression logs, see vulkan.md.
On macOS systems, Legolas++ leverages native Apple Metal compute (examples/VideoPipeline/VideoPipelineMetal.mm). Thanks to Apple Silicon's Unified Memory Architecture (UMA), the CPU and GPU share the same physical memory space:
- Zero PCIe Transfer Bottleneck: Frames and tensors are written directly into shared buffers (
MTLResourceStorageModeShared), eliminating upload and download overhead. - Peak End-to-End Speedup: Achieves 17,264 FPS on 32 concurrent 720p streams (57.15× speedup over scalar CPU, 7.57× faster than 8-core CPU), consuming only 2.43 µJ per frame.
Comprehensive tutorials, mathematical proofs, architecture guides, and API reference are available on the official documentation website:
🔗 https://laurentplagne.github.io/Legolas/
To preview the documentation site locally:
pip install -r requirements-docs.txt
mkdocs serve
# Open http://127.0.0.1:8000 in your browserLegolas++ is based on research presented at ACM SIGPLAN ARRAY:
Portable vectorization and parallelization of C++ multi-dimensional array computations
Laurent Plagne & Kavoos Bojnourdi
Proceedings of the 4th ACM SIGPLAN International Workshop on Libraries, Languages, and Compilers for Array Programming (ARRAY 2017), Pages 47–54.
DOI: 10.1145/3091966.3091973 · Available on ResearchGate
The foundational multi-target abstraction and data-structure concepts of Legolas++ originated in the PhD thesis of Wilfried Kirschenmann:
Vers des noyaux de calcul intensif pérennes (Towards sustainable high performance compute kernels)
Wilfried Kirschenmann (directed by Stéphane Vialle, advised by Laurent Plagne)
PhD Thesis in Computer Science, Université de Lorraine (2012), NNT: 2012LORR0203.
Available on HAL Theses · theses.fr
This project is distributed under the terms of the MIT "Expat" License (Copyright (c) 2019-2026 EDF-R&D, TriScale innov). See License.md for details.