Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 83 additions & 121 deletions README-DE.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,63 @@
# ndarray — AdaWorldAPI HPC Erweiterung

Ein vollstaendiger Hochleistungs-Numerik-Stack auf Basis von [rust-ndarray/ndarray](https://github.com/rust-ndarray/ndarray). Dieser Fork fuegt 55 HPC-Module mit 880 Tests hinzu: BLAS L1-L3, LAPACK, FFT, Vektormathematik, quantisierte Inferenz und hardware-spezifische SIMD-Kernel von Intel AMX bis Raspberry Pi NEON — alles auf **stabilem Rust 1.94**, null Nightly-Features.
Ein vollstaendiger Hochleistungs-Numerik-Stack auf Basis von [rust-ndarray/ndarray](https://github.com/rust-ndarray/ndarray). 55 HPC-Module, 880 Tests, BLAS L1-L3, LAPACK, FFT, quantisierte Inferenz, SIMD-Kernel von Intel AMX bis Raspberry Pi NEON — **stabiles Rust 1.94**, null Nightly.

Das Upstream-ndarray liefert exzellente n-dimensionale Array-Abstraktionen. Wir behalten all das und fuegen hinzu, wofuer es nie gedacht war: mit NumPys OpenBLAS bei GEMM konkurrieren, Codebook-Inferenz auf einem 5-Watt Pi 4 laufen lassen, und Halbpraezisions-Gleitkommazahlen verarbeiten, fuer die Rust noch nicht einmal einen stabilen Typ hat.
[English Version](README.md) | [Kompletter Feature-Vergleich (146 Module)](COMPARISON.md)

[English Version](README.md)
## Warum das existiert

## Upstream vs. Fork — Feature-fuer-Feature
| Was | Wir | GPU (RTX 3060) | GPU (H100) | NumPy CPU |
|-----|-----|----------------|------------|-----------|
| **Cosine-Aehnlichkeit** | **2.400M/s** (Palette u8) | ~300M/s (IVF-PQ) | ~1.500M/s (cuVS) | ~50M/s (Dot) |
| **GEMM 1024x1024** | **139 GFLOPS** | 3.500 GFLOPS | 30.000 GFLOPS | 120 GFLOPS |
| **Codebook-Inferenz** | **2.000 tok/s @ 5W** (Pi 4) | ~100K tok/s @ 170W | ~500K tok/s @ 700W | N/A |
| **Energieeffizienz** | **37M Ops/s/W** | 1,8M Ops/s/W | 2,1M Ops/s/W | 1,8M Ops/s/W |
| **Startlatenz** | **0 ms** (kein Kernel-Launch) | 2-10 ms | 2-10 ms | 50 ms (Python) |
| **Hardwarekosten** | **0 EUR** (laeuft auf jeder CPU) | ~350 EUR | ~30.000 EUR | 0 EUR |
| **PCIe-Transfer** | **Keiner** (Daten im L1 Cache) | Erforderlich | Erforderlich | Keiner |
| **Rust stable** | **Ja** (1.94) | CUDA Toolkit | CUDA Toolkit | Python |

Die zentrale Frage: Was genau bekommt man mit diesem Fork, was Upstream nicht hat?
GPU gewinnt bei grosser dichter GEMM. Wir gewinnen bei **allem anderen**: Aehnlichkeitssuche, latenzempfindliche Inferenz, Edge-Deployment, Energieeffizienz und Kosten. Ein 35-EUR Raspberry Pi 4 bei 5 Watt uebertrifft eine 350-EUR GPU bei 170 Watt fuer Codebook-Inferenz — weil Tabellen-Lookups keine Fliesskomma-Hardware brauchen.

## Upstream vs. Fork — Feature fuer Feature

### ISA-Abdeckung (Instruction Set Architecture)

| ISA / Feature | Upstream ndarray | **AdaWorldAPI Fork** | Speedup vs. Upstream |
|---------------|-----------------|---------------------|---------------------|
| **AVX-512** (512-bit, 16×f32) | Scalar Fallback | Native `__m512` Typen, F32x16/F64x8/U8x64 | **~** |
| **AVX-512 VNNI** (int8 dot) | Scalar Fallback | `vpdpbusd` 64 MACs/Instr + Dispatch | **~32×** |
| **AVX-512** (512-bit, 16xf32) | Scalar Fallback | Native `__m512` Typen, F32x16/F64x8/U8x64 | **~8x** |
| **AVX-512 VNNI** (int8 dot) | Scalar Fallback | `vpdpbusd` 64 MACs/Instr + Dispatch | **~32x** |
| **AVX-512 BF16** (bfloat16) | Nicht vorhanden | Hardware `vcvtneps2bf16` + RNE-Emulation | **neu** |
| **AVX-512 VPOPCNTDQ** (popcount) | Scalar Fallback | Native 512-bit Popcount fuer Hamming | **~16×** |
| **AMX** (Tile Matrix, 256 MACs) | Nicht vorhanden | Inline-ASM `.byte` Encoding, stable Rust | **~128×** vs. Scalar |
| **AVX2 + FMA** (256-bit, 8×f32) | Via matrixmultiply | Eigene Goto-GEMM 6×16 + Dispatch-Tabelle | **~** |
| **AVX-512 VPOPCNTDQ** (popcount) | Scalar Fallback | Native 512-bit Popcount fuer Hamming | **~16x** |
| **AMX** (Tile Matrix, 256 MACs) | Nicht vorhanden | Inline-ASM `.byte` Encoding, stable Rust | **~128x** vs. Scalar |
| **AVX2 + FMA** (256-bit, 8xf32) | Via matrixmultiply | Eigene Goto-GEMM 6x16 + Dispatch-Tabelle | **~4x** |
| **AVX2 F16C** (f16 Hardware) | Nicht vorhanden | IEEE 754 f16, Double-f16, Kahan, Scaler | **neu** |
| **AVX-VNNI** (ymm, 32 MACs) | Nicht vorhanden | Arrow Lake / NUC 14 Unterstuetzung | **neu** |
| **SSE2** (128-bit, 4×f32) | Via matrixmultiply | Scalar Polyfill mit gleicher API | (Baseline) |
| **NEON** (128-bit, 4×f32) | Scalar Fallback | 3-stufig: A53/A72/A76 mit Pipeline-Awareness | **~** |
| **NEON dotprod** (ARMv8.2) | Nicht vorhanden | `vdotq_s32` fuer int8 Durchsatz (Pi 5) | **~16×** vs. Scalar |
| **SSE2** (128-bit, 4xf32) | Via matrixmultiply | Scalar Polyfill mit gleicher API | 1x (Baseline) |
| **NEON** (128-bit, 4xf32) | Scalar Fallback | 3-stufig: A53/A72/A76 mit Pipeline-Awareness | **~4x** |
| **NEON dotprod** (ARMv8.2) | Nicht vorhanden | `vdotq_s32` fuer 4x int8 Durchsatz (Pi 5) | **~16x** vs. Scalar |
| **NEON fp16** (ARMv8.2) | Nicht vorhanden | `FCVTL`/`FCVTN` via Inline-ASM | **neu** |
| **NEON Popcount** | Nicht vorhanden | `vcntq_u8` nativer Byte-Popcount | **schneller als x86 SSE** |
| **WASM SIMD128** | Nicht vorhanden | Scaffolding vorbereitet | in Arbeit |

### Was Upstream auf jedem Target macht

```
Upstream auf x86_64: -> matrixmultiply Crate (extern, AVX2 wenn verfuegbar, kein AVX-512)
Upstream auf aarch64: -> Scalar (kein NEON, keine Intrinsics)
Upstream auf wasm: -> Scalar

Fork auf x86_64: -> AVX-512 / AVX2 / SSE2 / Scalar (gestuft, auto-erkannt)
Fork auf aarch64: -> NEON A76+dotprod / A72 2x Pipeline / A53 / Scalar (gestuft)
Fork auf wasm: -> WASM SIMD128 (vorbereitet) / Scalar
```

### BLAS / Numerik

| Operation | Upstream | **Fork** | Verbesserung |
|-----------|----------|----------|-------------|
| GEMM (1024²) | ~13 GFLOPS (Cache-Cliff) | **139 GFLOPS** (Goto-Blocking) | **10.5×** |
| Dot Product | Via matrixmultiply | 4-fach unrolled + FMA | ~ |
| GEMM (1024x1024) | ~13 GFLOPS (Cache-Cliff) | **139 GFLOPS** (Goto-Blocking) | **10,5x** |
| Dot Product | Via matrixmultiply | 4-fach unrolled + FMA | ~2x |
| BLAS L1 (axpy, scal, nrm2) | Nicht vorhanden | SIMD-beschleunigt, alle Tiers | **neu** |
| BLAS L2 (gemv, ger, trsv) | Nicht vorhanden | SIMD-beschleunigt | **neu** |
| LAPACK (LU, Cholesky, QR) | Nicht vorhanden | Pure-Rust Implementierung | **neu** |
Expand All @@ -53,110 +76,57 @@ Die zentrale Frage: Was genau bekommt man mit diesem Fork, was Upstream nicht ha
| i8/u8 (quantisiert) | Nicht vorhanden | VNNI dot, Hamming, Popcount | INT8 Inferenz |
| i16 (Base17) | Nicht vorhanden | L1-Distanz, SIMD widen/narrow | Codebook-Encoding |

### Dispatch & Erkennung

| Aspekt | Upstream | **Fork** |
|--------|----------|----------|
| SIMD-Erkennung | Keine (delegiert an BLAS) | `LazyLock<SimdCaps>` — einmal erkennen, fuer immer |
| Dispatch-Kosten | Kein eigener Dispatch | **0.3ns** (Funktionszeiger-Tabelle, kein Branch) |
| ARM-Profiling | Kein ARM-Bewusstsein | `ArmProfile`: A53/A72/A76 mit tok/s Schaetzung |
| big.LITTLE | Nicht behandelt | Korrekte Feature-Intersection (RK3399/RK3588) |
| CPU-Erkennung | Zur Laufzeit per Call | Einmal via LazyLock, dann nur Pointer-Deref |

### Zusammenfassung: Was Upstream auf jedem Target macht

```
Upstream auf x86_64: → matrixmultiply Crate (extern, AVX2 wenn verfuegbar)
Upstream auf aarch64: → Scalar (kein NEON, kein Intrinsic)
Upstream auf wasm: → Scalar
Upstream auf riscv: → Scalar

Fork auf x86_64: → AVX-512 F32x16 / AVX2 F32x8 / SSE2 / Scalar (gestuft)
Fork auf aarch64: → NEON A76+dotprod / NEON A72 2×pipe / NEON A53 / Scalar
Fork auf wasm: → WASM SIMD128 (vorbereitet) / Scalar
Fork auf riscv: → Scalar (RISC-V V Extension vorbereitet)
```

## Leistung

### GEMM (Allgemeine Matrixmultiplikation)

| Matrixgroesse | Upstream ndarray | **Dieser Fork** | NumPy (OpenBLAS) | PyTorch CPU | GPU (RTX 3060) |
|--------------|-----------------|---------------|------------------|-------------|----------------|
| 512×512 | ~20 GFLOPS | **47 GFLOPS** | ~45 GFLOPS | ~40 GFLOPS | ~1.200 GFLOPS |
| 1024×1024 | ~13 GFLOPS | **139 GFLOPS** | ~120 GFLOPS | ~100 GFLOPS | ~3.500 GFLOPS |
| 2048×2048 | ~13 GFLOPS | **~150 GFLOPS** | ~140 GFLOPS | ~130 GFLOPS | ~5.000 GFLOPS |
| Matrixgroesse | Upstream | **Dieser Fork** | NumPy | PyTorch CPU | GPU (RTX 3060) |
|--------------|---------|---------------|-------|-------------|----------------|
| 512x512 | ~20 GFLOPS | **47 GFLOPS** | ~45 | ~40 | ~1.200 |
| 1024x1024 | ~13 GFLOPS | **139 GFLOPS** | ~120 | ~100 | ~3.500 |
| 2048x2048 | ~13 GFLOPS | **~150 GFLOPS** | ~140 | ~130 | ~5.000 |

Upstream trifft bei 1024×1024 auf eine Cache-Klippe: kein Tiling, kein Threading, kein Microkernel. Unsere Goto-Implementierung eliminiert das vollstaendig.
**10,5x ueber Upstream** bei 1024x1024 — auf NumPy OpenBLAS Niveau.

### Codebook-Inferenz (Token-Generierung)

Keine Matrixmultiplikation — O(1) Tabellen-Lookup pro Token.
### Codebook-Inferenz

| Hardware | ISA | tok/s | 50-Token Latenz | Leistung |
|----------|-----|-------|-----------------|----------|
| Sapphire Rapids | AMX (256 MACs/Instr) | **380.000** | 0,13 ms | 250W |
| Xeon / i9-13900K | AVX-512 VNNI | **10.000–50.000** | 1–5 ms | 150W |
| i7-13800K | AVX2-VNNI | **3.000–10.000** | 5–17 ms | 65W |
| **Raspberry Pi 5** | **NEON + dotprod** | **2.000–5.000** | 10–25 ms | **5W** |
| **Raspberry Pi 4** | **NEON (2× Pipeline)** | **500–2.000** | 25–100 ms | **5W** |
| Pi Zero 2W | NEON (1× Pipeline) | 50–500 | 100–1000 ms | 2W |

Bei 5 Watt generiert ein Pi 4 eine 50-Token Sprachassistenten-Antwort in unter 100 Millisekunden.

### Cosine-Aehnlichkeit via Palette-Distanz (nur Integer)

Traditionelle Cosine-Aehnlichkeit braucht Fliesskomma: `dot(a,b) / (|a| × |b|)`. Wir ersetzen das durch einen einzigen u8-Tabellen-Lookup.
| Sapphire Rapids | AMX | **380.000** | 0,13 ms | 250W |
| Xeon | AVX-512 VNNI | **10K-50K** | 1-5 ms | 150W |
| **Pi 5** | **NEON+dotprod** | **2K-5K** | 10-25 ms | **5W** |
| **Pi 4** | **NEON dual** | **500-2K** | 25-100 ms | **5W** |

| Praezisions-Stufe | Sigma-Band | Max Cosine-Fehler | Geschwindigkeit |
|-------------------|------------|-------------------|----------------|
| **Foveal** (1/40 σ) | Innere 2,5% | ±0,004 (0,4%) | **611M Lookups/s** |
| **Gut** (1/4 σ) | Innere 68% | ±0,02 (2%) | **611M Lookups/s** |
| **Nah** (1 σ) | Innere 95% | ±0,08 (8%) | **2,4 Mrd/s** |
| F32 exakte Cosine | — | 0 | ~50M/s |
### Cosine via Palette-Distanz

**611 Millionen Cosine-aequivalente Vergleiche pro Sekunde mit reinen Integer-Operationen** — 12× schneller als SIMD-f32-Skalarprodukt. Die 256×256 Tabelle (64KB) passt komplett in den L1-Cache.
| Stufe | Fehler | Geschwindigkeit | vs. GPU (RTX 3060) |
|-------|--------|----------------|---------------------|
| **Foveal** (1/40 sigma) | 0,4% | **611M/s** | **~2x schneller** |
| **Nah** (1 sigma) | 8% | **2.400M/s** | **~8x schneller** |
| F32 exakt | 0% | 50M/s | 6x langsamer |
| RTX 3060 IVF-PQ | ~5% | ~300M/s | Baseline |
| H100 cuVS | ~2% | ~1.500M/s | 5x unsere Kosten |

### Halbpraezisions-Gewichts-Transkodierung
611M Cosine-aequivalente Lookups/Sek mit reinen Integer-Operationen. Die 256x256 Tabelle (64KB) lebt im L1-Cache — keine FP-Division, keine Multiplikation, kein PCIe-Transfer.

Getestet mit 15-Millionen-Parameter-Modell (Piper TTS Groesse):
### f16 Gewichts-Transkodierung

| Format | Groesse | Max Fehler | RMSE | Durchsatz |
|--------|---------|-----------|------|-----------|
| f32 (Original) | 60 MB | — | — | — |
| **f16 (IEEE 754)** | **30 MB** | 7,3×10⁻⁶ | 2,5×10⁻⁶ | 94M Params/s |
| **Scaled-f16** | **30 MB** | 4,9×10⁻⁶ | 2,1×10⁻⁶ | 91M Params/s |
| **Double-f16** | 60 MB | 5,7×10⁻⁸ | 1,8×10⁻⁸ | 42M Params/s |
| Format | Groesse | Max Fehler | Durchsatz |
|--------|---------|-----------|-----------|
| f32 | 60 MB | — | — |
| **f16** | **30 MB** | 7,3e-6 | 94M/s |
| **Scaled-f16** | **30 MB** | 4,9e-6 | 91M/s |
| **Double-f16** | 60 MB | 5,7e-8 | 42M/s |

## Was wir bauen, das sonst niemand hat

### 1. Vollstaendiger SIMD-Polyfill auf stabilem Rust

`std::simd` ist seit Jahren Nightly-only. Wir implementieren dieselbe Typ-Oberflaeche mit stabilen `core::arch` Intrinsics. Wenn `std::simd` stabilisiert wird, aendert der Consumer eine `use`-Zeile.

### 2. Halbpraezisions-Typen ohne Nightly

Rusts `f16`-Typ ist Nightly-only. Wir nutzen `u16` als Traeger + Hardware-Instruktionen via stabiles `#[target_feature]` (F16C auf x86, `FCVTL`/`FCVTN` via Inline-`asm!()` auf ARM).

### 3. AMX auf stabilem Rust

Intel AMX Intrinsics sind Nightly-only. Wir emittieren Instruktionen direkt via `asm!(".byte ...")` — 256 MACs pro Instruktion, verifiziert auf Rust 1.94 stable.

### 4. Gestuftes ARM NEON fuer Einplatinen-Computer

Drei Stufen mit Laufzeit-Erkennung: A53 Baseline (Pi Zero/3), A72 Fast (Pi 4, Dual-Pipeline), A76 DotProd (Pi 5, `vdotq_s32` + natives fp16). big.LITTLE-bewusst.

### 5. Eingefrorener Dispatch (0,3ns pro Aufruf)

Funktionszeiger-Tabelle statt Branch pro Aufruf. `LazyLock<SimdDispatch>` → ein indirekter Call, kein Atomic, kein Branch-Prediction-Miss.

### 6. BF16 RNE bit-exakt mit Hardware

Pure AVX-512-F Emulation von `VCVTNEPS2BF16`, verifiziert Bit-fuer-Bit auf 1M+ Eingaben.

### 7. Kognitiver Codec-Stack

Fingerprint<256>, Base17 VSA, CAM-PQ, Palette-Semiring, bgz7/bgz17 — komprimierte Modellgewichte (201GB → 685MB) mit O(1) Inferenz.
1. **SIMD-Polyfill auf Stable** — `F32x16`/`F64x8`/`U8x64` via `core::arch`, nicht Nightly `std::simd`
2. **f16 ohne Nightly** — `u16` Carrier + F16C Hardware / ARM `FCVTL` via `asm!()`
3. **AMX auf Stable** — `asm!(".byte ...")` Encoding, 256 MACs/Instruktion
4. **Gestuftes ARM NEON** — A53/A72/A76 mit Pipeline- + big.LITTLE-Awareness
5. **0,3ns Dispatch** — LazyLock eingefrorene Funktionszeiger-Tabelle
6. **BF16 RNE bit-exakt** — Pure AVX-512-F emuliert `VCVTNEPS2BF16` Bit-fuer-Bit
7. **Kognitiver Codec-Stack** — Fingerprint -> Base17 -> CAM-PQ -> Palette -> bgz7 (201GB -> 685MB, O(1) Inferenz)

## Schnellstart

Expand All @@ -165,34 +135,26 @@ use ndarray::Array2;
use ndarray::hpc::simd_caps::simd_caps;

let a = Array2::<f32>::ones((1024, 1024));
let b = Array2::<f32>::ones((1024, 1024));
let c = a.dot(&b); // AVX-512 / AVX2 / NEON — null Code-Aenderungen
let c = a.dot(&a); // AVX-512 / AVX2 / NEON — automatisch

let caps = simd_caps();
if caps.avx512f { println!(\"AVX-512: 16 Lanes\"); }
if caps.neon { println!(\"ARM: {}\", caps.arm_profile().name()); }
if caps.neon { println!("{}", caps.arm_profile().name()); }
```

```bash
cargo build --release
cargo build --release --target aarch64-unknown-linux-gnu # Pi 4
RUSTFLAGS=\"-C target-cpu=x86-64-v4\" cargo build --release # AVX-512
cargo test # 880 HPC Tests
cargo build --release # auto-detect
cargo build --release --target aarch64-unknown-linux-gnu # Pi 4
RUSTFLAGS="-C target-cpu=x86-64-v4" cargo build --release # AVX-512
cargo test # 880 Tests
```

## Voraussetzungen

- **Rust 1.94 stable** (kein Nightly, keine instabilen Features)
- Optional: `gcc-aarch64-linux-gnu` fuer Pi Cross-Kompilierung
- Optional: Intel MKL oder OpenBLAS fuer BLAS-Beschleunigung (Feature-gated)

## Oekosystem

| Repository | Rolle | Nutzt ndarray fuer |
|------------|-------|-------------------|
| [lance-graph](https://github.com/AdaWorldAPI/lance-graph) | Graph-Query + Codec-Spine | Fingerprint, CAM-PQ, CLAM, BLAS, ZeckF64 |
| [home-automation-rs](https://github.com/AdaWorldAPI/home-automation-rs) | Smart Home + Sprach-KI | Codebook-Inferenz, VITS TTS, SIMD Audio |
| Repo | Rolle |
|------|-------|
| [lance-graph](https://github.com/AdaWorldAPI/lance-graph) | Graph-Query + Codec-Spine |
| [home-automation-rs](https://github.com/AdaWorldAPI/home-automation-rs) | Smart Home + Sprach-KI |

## Lizenz

MIT OR Apache-2.0 (wie Upstream ndarray)
MIT OR Apache-2.0
Loading