std.cpu.dispatch — SIMD-Dispatch und f32-Arrays
Wählt zur Laufzeit den besten Rechen-Pfad (AVX2 → SSE2 → Scalar) und stellt float32-Arrays bereit, die 16-Byte-ausgerichtet im Speicher liegen. Der Dispatch basiert auf std.cpu.features.
→ Standard Library · std.cpu.features · Lyx-Dokumentation
Implementierungsstand: Diese Unit ist Teil von WP-GPU-02 des internen SIMD/GPU-Fahrplans. SSE2- und AVX2-Instruktionen werden vom Compiler-Codegen (CGN_BINOP SIMD) emittiert — die Stdlib-Funktionen hier sind das Dispatch-Gerüst und die Array-Verwaltung. Der Scalar-Fallback ist derzeit ein Placeholder (siehe Einschränkungen).
Import
import std.cpu.dispatch;
Zieht std.cpu.features automatisch mit.
Dispatch-Level
| Konstante | Wert | Bedeutung |
|---|---|---|
CPU_DISPATCH_SCALAR | 0 | Kein SIMD — reiner Scalar-Pfad |
CPU_DISPATCH_SSE2 | 1 | SSE2 verfügbar (128-Bit XMM-Register) |
CPU_DISPATCH_AVX2 | 2 | AVX2 verfügbar (256-Bit YMM-Register) |
fn CpuDispatchLevel(): int64
Gibt das höchste verfügbare Level zurück (AVX2 > SSE2 > Scalar). Das Ergebnis wird gecacht; /proc/cpuinfo wird nur beim ersten Aufruf gelesen.
SIMD-Array-Layout
SimdAlloc erzeugt ein f32-Array im Speicher mit folgendem Layout:
Adresse Inhalt
ptr-8 Elementanzahl (int64, 8 Byte)
ptr+0 Element 0 (f32, 4 Byte, 16-Byte-Grenze)
ptr+4 Element 1 (f32, 4 Byte)
ptr+8 Element 2 (f32, 4 Byte)
...
- Basis-Pointer wird per
mmap(anonym) angefordert:total = n×4 + 24Byte. - 16-Byte-Ausrichtung:
ptr = (base + 23) & ~15. - Elementanzahl steht bei
ptr-8(SimdLenliest dort). - f32-Werte werden als rohe 32-Bit-Bitmuster (
int64) übergeben — kein automatischer Cast.
Funktionen
SimdAlloc
fn SimdAlloc(n: int64): int64
Allokiert ein 16-Byte-ausgerichtetes f32-Array für n Elemente (via mmap). Gibt den ausgerichteten Pointer zurück, oder 0 bei Fehler. Elementanzahl wird bei ptr-8 gespeichert.
Kein SimdFree — SIMD-Arrays müssen mit MmapFree(ptr, n*4+24) manuell freigegeben werden. Alternativ werden sie beim Prozessende vom Kernel zurückgefordert.
SimdAdd / SimdSub / SimdMul / SimdDiv
fn SimdAdd(src1: int64, src2: int64): int64 // result = src1 + src2
fn SimdSub(src1: int64, src2: int64): int64 // result = src1 - src2
fn SimdMul(src1: int64, src2: int64): int64 // result = src1 * src2
fn SimdDiv(src1: int64, src2: int64): int64 // result = src1 / src2
Elementweise f32-Arithmetik. Jede Funktion:
- liest
naussrc1-8 - allokiert ein neues Array (
SimdAlloc(n)) - rechnet und schreibt in das neue Array
- gibt den Pointer auf das neue Array zurück
Der Aufrufer ist für die Freigabe aller Arrays (src1, src2, Ergebnis) verantwortlich.
SimdGet / SimdSet
fn SimdGet(arr: int64, i: int64): int64 // liest Element i als f32-Bits
fn SimdSet(arr: int64, i: int64, val: int64) // schreibt f32-Bits in Element i
Elementzugriff. Werte werden als rohe 32-Bit-Bitmuster (in int64) übergeben.
SimdLen
fn SimdLen(arr: int64): int64 // gibt n (Elementanzahl) zurück
Liest die Elementanzahl aus arr-8.
Codebeispiel
import std.cpu.dispatch;
import std.io;
fn main() {
// Dispatch-Level ermitteln
var lvl: int64 := CpuDispatchLevel();
if (lvl == CPU_DISPATCH_AVX2) {
PrintLn("Dispatch: AVX2");
} else if (lvl == CPU_DISPATCH_SSE2) {
PrintLn("Dispatch: SSE2");
} else {
PrintLn("Dispatch: Scalar");
}
// f32-Array belegen: Bits von 1.0 = 0x3F800000, 2.0 = 0x40000000
var a: int64 := SimdAlloc(4);
var b: int64 := SimdAlloc(4);
SimdSet(a, 0, 0x3F800000); // 1.0
SimdSet(a, 1, 0x3F800000); // 1.0
SimdSet(a, 2, 0x3F800000); // 1.0
SimdSet(a, 3, 0x3F800000); // 1.0
SimdSet(b, 0, 0x40000000); // 2.0
SimdSet(b, 1, 0x40000000); // 2.0
SimdSet(b, 2, 0x40000000); // 2.0
SimdSet(b, 3, 0x40000000); // 2.0
var result: int64 := SimdAdd(a, b);
PrintLn(IntToStr(SimdLen(result)) + " Elemente");
// Elemente als Bits auslesen
PrintLn("result[0] bits: 0x" + IntToStr(SimdGet(result, 0)));
}
Hinweis: f32-Bits werden als rohe Integerwerte gehandhabt. Zum menschenlesbaren Ausgeben müssen die Bits als f64 interpretiert werden — eine Helper-Unit für f32⟺int64-Konversionen gibt es aktuell nicht in der Stdlib.
Implementierungshinweise
Der Scalar-Fallback (SimdAddScalar u. a.) konvertiert die f32-Bitbitmuster mit as int64 in Integer, dann mit as f64 in Float und rechnet damit. Das ergibt korrekte Werte nur dann, wenn die IEEE-754-Bitmuster und die numerischen Werte übereinstimmen — was bei regulären f32-Zahlen nicht der Fall ist. Zum Beispiel wird 1.0f (Bits 0x3F800000 = 1065353216) als Float 1065353216.0 interpretiert.
Die eigentlichen SIMD-Instruktionen (ADDPS, VADDPS) werden nicht von diesen Stdlib-Funktionen emittiert, sondern vom Compiler-Codegen (CGN_BINOP SIMD), wenn der Compiler f32-Ausdrücke auf SIMD-Arrays erkennt. Diese Stdlib-Funktionen sind das Gerüst für expliziten Library-Code außerhalb des Compiler-optimierten Pfades.
Einschränkungen
| Thema | Details |
|---|---|
| Scalar-Fallback buggy | f32-Bits werden als Integer-Werte addiert/subtrahiert — kein korrektes f32-Rechnen |
| SSE2-Pfad = Scalar | SimdAdd mit SSE2 ruft intern SimdAddScalar auf (SSE2 via Codegen, nicht stdlib) |
| Kein SimdFree | Manuell per MmapFree freigeben oder Leak akzeptieren |
| f32 als Bits | SimdGet/Set liefert rohe 32-Bit-Bitmuster in int64 — kein Typ-Safety |
| Linux only | Nutzt mmap + CpuFeatureDetect (proc/cpuinfo) |
| Keine Bounds-Checks | SimdGet(arr, i) prüft nicht ob i < SimdLen(arr) |
| Entwicklungsstand | WP-GPU-02; keine pub-Deklarationen; Import-Pfad src.std.* |
| GPU-Units | std.gpu.* existiert noch nicht (WP-GPU-07–09, geplant) |
Letzte Aktualisierung: 2026-06-13
