====== 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 [[lyx_-_programmiersprache:units:cpu:features|std.cpu.features]].
→ [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:cpu:features|std.cpu.features]] · [[lyx_-_programmiersprache:start|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|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 + 24'' Byte.
* 16-Byte-Ausrichtung: ''ptr = (base + 23) & ~15''.
* Elementanzahl steht bei ''ptr-8'' (''SimdLen'' liest 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 ''n'' aus ''src1-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