Skip to content

knowledge: den Polyfill in den Browser bringen — wasm32 SIMD128, und die zwei Arten, auf die es still nichts tut - #278

Merged
AdaWorldAPI merged 1 commit into
masterfrom
claude/wasm-simd-guide
Aug 14, 2026
Merged

knowledge: den Polyfill in den Browser bringen — wasm32 SIMD128, und die zwei Arten, auf die es still nichts tut#278
AdaWorldAPI merged 1 commit into
masterfrom
claude/wasm-simd-guide

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

.claude/knowledge/wasm-simd-consumer-guide.md — wie ein Consumer ndarray::simd auf wasm32 tatsächlich bekommt. Eine von drei Hälften; die Companions liegen in a2ui-rs (Rezept) und OGAR (Substrat).

Warum ausgerechnet wasm ein eigenes Dokument verdient

Auf x86 und ARM ist das Polyfill-Versprechen fast gratis: ein Autovektorisierer hat Register, auf die er zielen kann, also wird eine Skalarschleife oft zufällig SIMD und niemand merkt den Unterschied.

Auf wasm32 gibt es ohne +simd128 überhaupt keine Vektorregister. Der Autovektorisierer kann nichts finden, egal wie gut die Schleife geformt ist. Der Browser ist damit der eine Ort, an dem „wir verlassen uns auf LLVM" bis zum Skalar durchfällt — ohne Fehler, ohne Warnung, mit korrekten Ergebnissen.

Das macht wasm zugleich zum stärksten Argument für die Polyfill-Regel und zum leichtesten Ort, an dem man glaubt, ihr gefolgt zu sein.

Das Rezept, mit den zwei Stolpersteinen, die je einen Build kosten

  • Der Default-Branch ist master, nicht mainbranch = "main" scheitert mit cannot locate remote-tracking branch.
  • default-features = false allein entfernt simd: das Modul liegt hinter std (#[cfg(feature = "std")] pub mod simd), und der Fehler (unresolved import ndarray::simd) nennt die Ursache nicht.

Dazu: [patch] für lokale Sibling-Arbeit gehört in .cargo/config.toml, nicht ins Manifest — ein Pfad im Manifest wandert in CI- und Container-Builds, die keinen Sibling haben.

Die zwei stillen Fehlschläge

(a) Das Flag fehlt — Skalar-Fallback, korrekte Ergebnisse, null Vektoren. Das ist bewusst so (der Code muss auch ohne SIMD-Unterstützung funktionieren), und genau deshalb beschwert sich nichts.

(b) Der Code ist gar nicht im Artefakt. Ein cdylib behält nur exportierte Items. Gemessener Fall: 2 SIMD-Instruktionen und null Layout-Symbole im ausgelieferten Modul, während derselbe Code als rlib 800 in einer Funktion trug. Wenn eine wasm-SIMD-Messung nahe null herauskommt, erst prüfen ob der Code drin ist — dann erst auf das Flag schließen.

Und die Mess-Falle, die ich selbst gestellt habe

Eine Funktion nicht über ein awk-Fenster um ihren Namen isolieren. Das ist keine Funktionsgrenze, und die rlib steckt voller ndarray-eigener vektorisierter Funktionen — die Zahl kann fast vollständig fremder Code sein. Symboltabelle fragen (llvm-nm + --disassemble-symbols).

Die Textfenster-Form gab 801 wo die symbol-genaue Antwort 800 ist. Aus Glück richtig — und eine Messung, die aus Glück stimmt, ist keine Messung.

Semantik-Abweichungen des wasm32-Backends

Als Tabelle, weil ein Parity-Test über mehrere Backends sie tolerieren muss: mul_add ohne FMA (zwei Rundungen, außer mit relaxed-simd), Baum- statt Folge-Reduktion, round-half-to-even, NaN-Propagation in min/max. Konsequenz: relative Toleranz statt Gleichheit, und die Skalar-Referenz sollte dieselbe Funktion sein, die der Tail benutzt — eine Definition, damit die beiden nicht driften können.

Alles davon steht bereits in src/simd_wasm.rs; hier ist es aus Consumer-Sicht zusammengezogen, mit der Test-Konsequenz daneben.


Format nach dem Vorbild von vertical-simd-consumer-contract.md (READ BY: / P0 TRIGGERS:). Nur Doku, kein Code.


Generated by Claude Code

.claude/knowledge/wasm-simd-consumer-guide.md -- wie ein Consumer
ndarray::simd auf wasm32 tatsaechlich bekommt, und die zwei Arten, auf
die es still nichts tut.

Warum ausgerechnet wasm: auf x86 und ARM ist das Polyfill-Versprechen
fast gratis, weil ein Autovektorisierer Register hat, auf die er zielen
kann -- eine Skalarschleife wird oft zufaellig SIMD und niemand merkt
den Unterschied. Auf wasm32 gibt es OHNE +simd128 ueberhaupt keine
Vektorregister. Der Browser ist also der eine Ort, an dem "wir
verlassen uns auf LLVM" bis zum Skalar durchfaellt, ohne Fehler und
ohne Warnung.

Das Rezept, mit den zwei Stolpersteinen, die je einen Build kosten wenn
man sie raet: der Default-Branch ist master, nicht main; und
default-features = false entfernt simd, weil das Modul hinter std
liegt.

Die zwei stillen Fehlschlaege: (a) das Flag fehlt -- Skalar-Fallback,
korrekte Ergebnisse, null Vektoren; (b) der Code ist gar nicht im
Artefakt, weil ein cdylib nur exportierte Items behaelt. Gemessener
Fall: 2 SIMD-Instruktionen und null Layout-Symbole im ausgelieferten
Modul, waehrend derselbe Code als rlib 800 in einer Funktion trug.

Und die Mess-Falle, die ich selbst gestellt habe: eine Funktion NICHT
ueber ein awk-Fenster um ihren Namen isolieren. Das ist keine
Funktionsgrenze, und die rlib steckt voller ndarray-eigener
vektorisierter Funktionen -- die Zahl kann fast vollstaendig fremder
Code sein. Symboltabelle fragen (llvm-nm + --disassemble-symbols).

Dazu die Tabelle der Semantik-Abweichungen des wasm32-Backends
(mul_add ohne FMA, Baum- statt Folge-Reduktion, round-half-to-even,
NaN in min/max), weil ein Parity-Test ueber mehrere Backends sie
tolerieren muss -- relative Toleranz statt Gleichheit, und die
Skalar-Referenz sollte dieselbe Funktion sein, die der Tail benutzt.

Format nach dem Vorbild von vertical-simd-consumer-contract.md
(READ BY / P0 TRIGGERS).
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Important

Review available on request

  • 🔍 Trigger review

Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment @coderabbitai review to review the latest changes. For a full review, comment @coderabbitai full review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e6dd9045-e9f0-4bbf-a089-0e4079c73544


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_27388446-b794-4060-b61a-e3a69b8c0bdf)

@AdaWorldAPI
AdaWorldAPI merged commit e0a7e1c into master Aug 14, 2026
20 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 338325c535

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +73 to +76
or use the checked-in profile, which exists for exactly this:

```bash
cargo build --target wasm32-unknown-unknown --config .cargo/config-wasm.toml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Point consumers at a config they actually own

When this recipe is followed from a consumer that declares ndarray as a Git dependency, .cargo/config-wasm.toml exists only inside ndarray's cached checkout, not at this path in the consumer. Cargo resolves the --config argument from the invoking workspace, so this alternative command fails unless the consumer independently copied the file; retain the RUSTFLAGS command or explicitly instruct consumers to create/check in an equivalent config.

Useful? React with 👍 / 👎.

Comment on lines +211 to +215
So the honest shape of a vector/scalar parity test is a **relative
tolerance**, not equality:

```rust
assert!((a - b).abs() <= 1e-4 * b.abs().max(1.0));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use operation-aware cross-backend assertions

For the exact divergent inputs listed immediately above, this relative-tolerance assertion does not provide parity: a half-way round case differs by 1, and a wasm min/max producing NaN makes the comparison false even when the scalar backend intentionally returns the numeric operand. Consumers following this recipe will therefore write tests that fail on valid backend behavior; the guide should prescribe explicit handling or exclusion of ties and NaNs, reserving numerical tolerance for reduction/FMA rounding differences.

Useful? React with 👍 / 👎.

and the scalar reference should be **the same function the tail uses** —
one definition, so the two cannot drift apart.

Enabling `relaxed-simd` restores a fused `mul_add` via `f32x4_relaxed_madd`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid promising fusion for relaxed SIMD

When a runtime selects the permitted unfused behavior for f32x4.relaxed_madd, enabling relaxed-simd does not restore fused semantics. The repository already records this explicitly in src/simd_ops.rs:969-971: fusion is implementation-defined and the same wasm binary may round differently across runtimes. Calling it fused can lead consumers to rely on FMA results or determinism that the backend does not guarantee.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants