Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
06b7c6a
[Test] Pin behaviour for @qd.data_oriented with raw qd.ndarray members
hughperkins May 16, 2026
d4350ef
[Fix] Recurse through nested data_oriented / dataclass children when …
hughperkins May 16, 2026
97afa6d
[Fix] Launch-context stale guard fires for @qd.data_oriented containe…
hughperkins May 16, 2026
49a723b
[Test] Extend @qd.data_oriented + ndarray coverage: cross-container n…
hughperkins May 16, 2026
9bdeca5
[Doc] @qd.data_oriented can contain ndarrays
hughperkins May 16, 2026
dc7997b
[Fix] Gap A: template-mapper spec key descends into data_oriented nda…
hughperkins May 16, 2026
906ce19
[Test] Gap A: spec-key descent into data_oriented ndarray members
hughperkins May 16, 2026
a0db648
[Fix] Template-mapper args_hash invalidates when data_oriented ndarra…
hughperkins May 16, 2026
c9598ad
[Fix] Clear error for @qd.data_oriented field type inside typed-datac…
hughperkins May 16, 2026
93893e5
[Perf] Per-class cache of data_oriented ndarray attribute paths for G…
hughperkins May 16, 2026
ce769a7
[Doc] Nesting compatibility matrix for compound types + spot tests
hughperkins May 16, 2026
dd4de40
[Doc] Fix @qd.struct ghost reference in compound_types
hughperkins May 16, 2026
46825ab
[Test] Pin fastcache + @qd.data_oriented + ndarray end-to-end behavior
hughperkins May 16, 2026
ee5fbbb
[Doc] Fastcache with @qd.data_oriented: worked example, semantics, fo…
hughperkins May 16, 2026
b132b81
[Doc] Restructure fastcache.md: simple main body, Advanced subsection…
hughperkins May 16, 2026
6d1c820
[Doc] Use 'member' consistently for compound-type members; drop ambig…
hughperkins May 16, 2026
1de65b9
[Doc] Mirror qd.Template wording for @qd.data_oriented primitive memb…
hughperkins May 16, 2026
a648c3f
[Doc] @qd.data_oriented row: 'types and values' to mirror qd.Template…
hughperkins May 16, 2026
a55d360
[Doc] Tighten path-cache stability restriction: actual failure modes …
hughperkins May 16, 2026
6667ba6
[Test] Fix fastcache cross-init tests: filter captured launches by ke…
hughperkins May 16, 2026
e9c50b4
[Style] pre-commit auto-fixes: black wrap + ruff import-sort
hughperkins May 16, 2026
abf242b
[Doc] Move @qd.kernel inside @qd.data_oriented class in the ndarray-m…
hughperkins May 17, 2026
4c27e2e
[Doc] Document primitive members on @qd.data_oriented self as templat…
hughperkins May 17, 2026
1f539e6
[Doc] State ndarray-member subscript behaviour directly instead of cr…
hughperkins May 17, 2026
730cbcb
[Doc] Drop 'as with dataclasses.dataclass' cross-reference in ndarray…
hughperkins May 17, 2026
57e1b95
[Doc] Simplify fastcache cross-link in @qd.data_oriented section: dro…
hughperkins May 17, 2026
d4ca211
[Doc] Drop ndarray-reassign note and tighten fastcache cross-link in …
hughperkins May 17, 2026
b72a7a7
[Doc] Drop ndarray subscript-access description in @qd.data_oriented …
hughperkins May 17, 2026
18ff7bd
[Doc] Promote fastcache cross-link to its own ### Fastcache subsectio…
hughperkins May 17, 2026
33f4744
[Doc] Rename '### ndarray members' to '### Tensor members'; cover qd.…
hughperkins May 17, 2026
883243e
[Doc] @qd.data_oriented Fastcache subsection: spell out 'disabled for…
hughperkins May 17, 2026
3504250
[Doc] Tensor members: shorten qd.tensor description to 'or qd.Tensor'
hughperkins May 17, 2026
cc01339
[Doc] Tensor members: simplify nested-container sentence to 'Nested @…
hughperkins May 17, 2026
df3113e
[Doc] Fastcache subsection: 'methods of @qd.data_oriented classes'
hughperkins May 17, 2026
7f5fd12
[Doc] Tensor members: drop qd.Vector.ndarray / qd.Matrix.ndarray pare…
hughperkins May 17, 2026
e7fafeb
[Doc] Tensor members: drop the mixing-backends + nesting trailer sent…
hughperkins May 17, 2026
f9a35df
[Doc] Restrictions: drop redundant 'A few combinations are still unsu…
hughperkins May 17, 2026
d336dcd
[Doc] @qd.dataclass section opener: cut to the constraint
hughperkins May 17, 2026
4c5f622
[Doc] Remove top-level Recommendation section
hughperkins May 17, 2026
56a4399
[Doc] Expand @qd.dataclass section: what it does, when to use it, con…
hughperkins May 17, 2026
ef5f8a6
[Doc] @qd.dataclass section: drop use-cases / constraints / cross-ref…
hughperkins May 17, 2026
06580f1
[Doc] @qd.dataclass section opener: explain the kernel-side vs python…
hughperkins May 17, 2026
8899357
[Doc] Restore verbatim prose for the @qd.struct vs other-compound-typ…
hughperkins May 17, 2026
8fef507
[Doc] Replace @qd.struct with @qd.dataclass in opener prose (actual A…
hughperkins May 17, 2026
92f5fe1
[Doc] @qd.dataclass: 'element type of fields' not 'tensors'
hughperkins May 17, 2026
9ea8e5b
[Doc] @qd.dataclass: add sentences about @qd.func methods and qd.type…
hughperkins May 17, 2026
6ff0848
[Doc] @qd.dataclass methods sentence: 'Methods can be added to ... an…
hughperkins May 17, 2026
fd8cd0a
[Doc] @qd.dataclass section: move qd.types.struct paragraph to end wi…
hughperkins May 17, 2026
004cd9a
[Doc] qd.types.struct sentence: drop 'useful when members are compute…
hughperkins May 17, 2026
bf85e4e
[Doc] @qd.dataclass: split into bare-struct example, then methods + @…
hughperkins May 17, 2026
ccaae54
[Doc] First @qd.dataclass example uses AOS layout (the unique-to-Stru…
hughperkins May 17, 2026
820c01a
[Doc] Move 'Nesting compatibility' section to end of compound_types.md
hughperkins May 17, 2026
06d2e86
[Doc] Overview table: dataclasses.dataclass supports differentiation …
hughperkins May 17, 2026
f7dd090
[Test] AD through dataclasses.dataclass with ndarray, field, and qd.t…
hughperkins May 17, 2026
8c0377c
[Doc] compound_types: rephrase intro bullets to describe each type's …
hughperkins May 17, 2026
71a53da
[Doc] compound_types: prefix dataclasses.dataclass with @ in intro/ta…
hughperkins May 17, 2026
46fef24
[Test] AD dataclass: tensor(FIELD) member works when annotated as qd.…
hughperkins May 17, 2026
18f995b
[Doc] tensor: note qd.Tensor is also the dataclass-member annotation
hughperkins May 17, 2026
3ce0ab0
[Doc] compound_types: add 'Under the hood' subsection for each type
hughperkins May 17, 2026
35be370
[Doc] compound_types: rewrite 'Under the hood' subsections at a highe…
hughperkins May 17, 2026
94e455a
[Doc] compound_types: drop 'once' from compile-time capture phrasing
hughperkins May 17, 2026
31b27d7
[Doc] compound_types: replace overview table with differentiating one
hughperkins May 17, 2026
36dc933
[Doc] compound_types: drop 'historical reasons' line
hughperkins May 17, 2026
07dc486
[Fix] _build_struct_nd_paths: handle NamedTuple via _asdict() fallback
hughperkins May 18, 2026
c7d6737
[wip] preserved baseline: stable_members mitigations + new failing test
May 17, 2026
93f597e
[fix] Option A: expand dataclass-instance args in @qd.func calls from…
May 17, 2026
c25f49c
[test] nested dataclasses + chained @qd.func calls from data_oriented…
May 17, 2026
8f64016
[Perf] Prune unused @qd.data_oriented ndarrays via existing pruning m…
hughperkins May 17, 2026
aa9a88f
[Fix] Fastcache hasher: skip QuadrantsCallable/BoundQuadrantsCallable…
hughperkins May 17, 2026
fd8c440
[Perf] Don't over-mark ndarrays during @qd.func dataclass-arg expansion
hughperkins May 17, 2026
e3a3d88
[Perf] TemplateMapper.lookup: only walk template-slot args, cache per…
hughperkins May 18, 2026
067a471
[Fix] Walker robustness: cycle-safe + Pydantic-metaclass-safe is_data…
hughperkins May 18, 2026
cc1e380
[Style] Apply pre-commit (black + ruff): import order, single-line co…
hughperkins May 18, 2026
34f8532
[Fix] stable_members: tolerate opaque members in fastcache hasher + c…
hughperkins May 18, 2026
5e54902
[Fix] stable_members fastcache: only tolerate truly-opaque members, f…
hughperkins May 18, 2026
55ecf95
[Fix] Metaclass-safe is_dataclass for walker over user objects
hughperkins May 18, 2026
6d9c307
[Style] pre-commit: import formatting
hughperkins May 18, 2026
49ffb3b
[Fix] Fastcache: skip opaque-typed members silently by default
hughperkins May 18, 2026
7757907
[Doc] Fastcache: opaque-member silencing is the default; clarify stab…
hughperkins May 18, 2026
fb38fec
Revert "[Doc] Fastcache: opaque-member silencing is the default; clar…
hughperkins May 18, 2026
7cabaa0
Revert "[Fix] Fastcache: skip opaque-typed members silently by default"
hughperkins May 18, 2026
b5b360a
[Fix] Fastcache: replace PARAM_INVALID / silent-skip with qualname fa…
hughperkins May 18, 2026
3aa4fe1
[Fix] test_ad_dataclass: require data64 extension for f64 tests
hughperkins May 18, 2026
dce1305
[Refactor] Fastcache: two-level cache + pruning-driven narrow args walk
hughperkins May 18, 2026
984ac40
[Doc] Fastcache: pruning-driven semantics; stable_members is launch-p…
hughperkins May 18, 2026
12fb215
[Fix] Fastcache: full pruning coverage for data_oriented; remove qual…
hughperkins May 18, 2026
356394e
[Test] Pin pruning-driven fastcache behaviour for @qd.data_oriented args
hughperkins May 18, 2026
45129bc
[Doc] data_oriented(stable_members=...) docstring: correct the failur…
hughperkins May 18, 2026
1f25d9c
[Fix] record_after_call: propagate chain paths through Attribute args
hughperkins May 18, 2026
5fc9b4c
[Fix] Track @qd.func params in fn_param_names for chain-path seeding
hughperkins May 18, 2026
89bb005
[Style] test docstrings: reflow at 120c per repo line-width
hughperkins May 18, 2026
710ee47
[Fix] Fastcache: prune _predeclare_struct_ndarrays by flat-name on ca…
hughperkins May 18, 2026
090f1a8
[CI] Fix linters, pyright, MockContext test, deleted-comment, line-wrap
hughperkins May 18, 2026
be4b030
[Refactor] Move fold_*_into_pruning from Kernel to Pruning
hughperkins May 19, 2026
75c08f6
[Style] Reflow 3 docstring paragraphs to 120c (Check line wrapping)
hughperkins May 19, 2026
29dd841
[Style] Reflow 3 more comment/docstring lines to 120c
hughperkins May 19, 2026
4bd2d10
[Style] Reflow 3 more comment lines to 120c
hughperkins May 19, 2026
aef1a26
[Style] Manually reflow underwrapped prose to 120c
hughperkins May 19, 2026
197d150
[Style] Reflow more underwrapped prose to 120c (round 2)
hughperkins May 19, 2026
173b051
Merge remote-tracking branch 'origin/hp/data-oriented-ndarray-fix' in…
hughperkins May 19, 2026
a47a5ab
[Doc] fastcache.md: restore prose phrasing in unsupported-type + arg-…
hughperkins May 19, 2026
5debfe4
[Doc] fastcache.md: drop redundant 'every child is subject to pruning…
hughperkins May 19, 2026
4e714c7
[Doc] fastcache.md: revert @qd.data_oriented child-rule bullets to or…
hughperkins May 19, 2026
39602c6
[Doc] fastcache.md: tighten recognised-but-unsupported sentence
hughperkins May 19, 2026
bd37c94
[Doc] fastcache.md: restore nested-dataclass + qd.field bullets in da…
hughperkins May 19, 2026
59ce5ff
[Style] args_hasher: restore original 'field offset' comments on Scal…
hughperkins May 19, 2026
a63b834
[Docs] src_hasher: remove pre-refactor background paragraph from modu…
hughperkins May 19, 2026
f6c68d8
[Docs] src_hasher: correct safety-implication paragraph
hughperkins May 19, 2026
ae36b11
[Fix] Per-instance ndarray-path cache for @qd.data_oriented args
hughperkins May 19, 2026
c61d32c
[Test] Strengthen polymorphism + add cache-hit predeclare ndarray test
hughperkins May 19, 2026
706f9b5
[Test] Add bug reproducer: needs_grad not folded into fastcache args_…
hughperkins May 19, 2026
4398af7
[Fix] Fold needs_grad into fastcache narrow args_hash for ndarray leaves
hughperkins May 19, 2026
8a7ead4
[Lint] Reorder imports in needs_grad reproducer test
hughperkins May 19, 2026
8861dc0
Merge remote-tracking branch 'origin/main' into hp/data-oriented-qd-f…
hughperkins Jun 3, 2026
e8bcd18
[Doc] Fix tile16 -> tile link rename; reflow under-wrapped CI flags
hughperkins Jun 3, 2026
44a535f
Merge origin/main into hp/data-oriented-qd-func-dataclass (post-#723)
hughperkins Jun 5, 2026
6805ce3
Merge branch 'main' into hp/data-oriented-qd-func-dataclass
hughperkins Jun 11, 2026
e4bf125
[Doc] fastcache.md: simplify per Hugh review #r3399480151
hughperkins Jun 11, 2026
2719dca
[Doc] fastcache.md: narrowing applies to all compound types, not just…
hughperkins Jun 12, 2026
4a97b91
[Doc] fastcache.md: address Hugh review r3403946029
hughperkins Jun 12, 2026
cfeac8b
[Doc] compound_types.md: prominent stable_members recommendation
hughperkins Jun 12, 2026
ed3b6c2
Merge branch 'main' into hp/data-oriented-qd-func-dataclass
hughperkins Jun 12, 2026
0415c05
Merge branch 'main' into hp/data-oriented-qd-func-dataclass
hughperkins Jun 12, 2026
9fb8287
remove internal stuff
hughperkins Jun 17, 2026
66d095f
[Refactor] Extract fastcache L1/L2 persistence orchestration to src_h…
hughperkins Jun 17, 2026
0401c7a
[Style] test_data_oriented_ndarray: reflow under-wrapped comments to …
hughperkins Jun 17, 2026
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
27 changes: 27 additions & 0 deletions docs/source/user_guide/compound_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,31 @@ sim.step()

`@qd.data_oriented` objects can also be passed as `qd.Template` parameters to kernels defined outside the class, and they support nesting (one `@qd.data_oriented` struct containing another).

### stable_members

**Recommended for any `@qd.data_oriented` class whose ndarray members are allocated once (typically in `__init__`) and not subsequently rebound — the common case.** Decorate with `stable_members=True`:

```python
@qd.data_oriented(stable_members=True)
class Simulation:
def __init__(self, n):
self.x = qd.ndarray(qd.f32, shape=(n,))
self.v = qd.ndarray(qd.f32, shape=(n,))
# ... more ndarray / field / primitive members
```

This skips a per-call walk that Quadrants otherwise runs to detect ndarray member rebinding between kernel launches. The walk is O(number of ndarray members) per kernel call, so the savings scale with the container's size.

Microbenchmark on an RTX PRO 6000 Blackwell with a container holding 30 `qd.ndarray` members across two nesting levels, calling a trivial kernel that takes the container as a `qd.template()` arg:

| | Per-launch Python overhead |
|---|---|
| `stable_members=False` (default) | 18.5 µs/call |
| `stable_members=True` | 13.5 µs/call |
| | **−5 µs/call (−28%)** |

**Trade-off:** with `stable_members=True`, reassigning an ndarray member on an instance is undefined behavior — the previously compiled kernel will be reused even if the new ndarray has a different `dtype`, `ndim`, or layout, silently bit-reinterpreting the new array's storage. Set it only on classes whose ndarray members are allocated once (typically in `__init__`) and never rebound. See [Reassigning ndarray members](#reassigning-ndarray-members) below for the supported alternative.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe this behaviour should be enforced via post-init freeze. But I think it is tricky to get right without annoying boilerplate on user-side and/or performance penalty.

Even Python dataclass does not offer a clean way to customise init when frozen.

https://docs.python.org/3/library/dataclasses.html#dataclasses

frozen: If true (the default is False), assigning to fields will generate an exception. This emulates read-only frozen instances. See the [discussion](https://docs.python.org/3/library/dataclasses.html#dataclasses-frozen) below.

If [__setattr__()](https://docs.python.org/3/reference/datamodel.html#object.__setattr__) or [__delattr__()](https://docs.python.org/3/reference/datamodel.html#object.__delattr__) is defined in the class and frozen is true, then [TypeError](https://docs.python.org/3/library/exceptions.html#TypeError) is raised.


### Primitive members

Primitive members on `self` (e.g. `int`, `float`, `bool`, `enum.Enum`) are supported, but they are treated as **template values**: each distinct primitive value across instances triggers a new kernel compilation, with the value baked into the kernel IR.
Expand Down Expand Up @@ -318,6 +343,8 @@ Practical consequence:

For `@qd.data_oriented` containers passed via `qd.Template`, reassigning an ndarray member between kernel launches is supported, including changes to `dtype`, `ndim`, or layout. A new specialised kernel is compiled and cached for the new shape; subsequent launches with the original shape continue to use the original cached kernel. (For `@dataclasses.dataclass` containers — passed via the dataclass-type annotation — the member binding follows the standard dataclass mutability rules: frozen dataclasses can't rebind, non-frozen ones can, and a rebind triggers a fresh kernel arg setup on the next launch.)

This support is only available on `@qd.data_oriented` classes *without* the [`stable_members=True`](#stable_members) opt-in. Setting `stable_members=True` is a promise that ndarray members on instances of the class are never reassigned; if you break that promise the previously compiled kernel is silently reused against the new ndarray.

### Restrictions

- **`@qd.dataclass` cannot contain `qd.ndarray` or `qd.field` members.** See the [`@qd.dataclass`](#qddataclass-qdtypesstruct) section above for the full list of allowed member types. (The function-form factory `qd.types.struct(...)` has the same restrictions.)
Expand Down
56 changes: 29 additions & 27 deletions docs/source/user_guide/fastcache.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,17 @@ Fastcache supports the following parameter types:
| `qd.types.NDArray` (scalar, vector, matrix) | Yes | dtype, ndim, layout |
| `torch.Tensor` | Yes | dtype, ndim |
| `numpy.ndarray` | Yes | dtype, ndim |
| `dataclasses.dataclass` | Yes | member types recursively; member values if annotated with `FIELD_METADATA_CACHE_VALUE` (see [Appendix — compound-type cache keying](#compound-type-cache-keying)) |
| `@qd.data_oriented` objects | Yes | member types recursively; primitive member types and values baked into kernel (see [Appendix — compound-type cache keying](#compound-type-cache-keying)) |
| `dataclasses.dataclass` | Yes | member types recursively (narrowed to members the kernel reads or writes); member values if annotated with `FIELD_METADATA_CACHE_VALUE` (see [Advanced — compound-type cache keying](#compound-type-cache-keying)) |
| `@qd.data_oriented` objects | Yes | member types recursively (narrowed to members the kernel reads or writes); primitive member types and values baked into kernel (see [Advanced — compound-type cache keying](#compound-type-cache-keying)) |
| `qd.Template` primitives (int, float, bool) | Yes | type and value (baked into kernel) |
| Non-template primitives (int, float, bool) | Yes | type only |
| `enum.Enum` | Yes | name and value |
| `qd.field` / `ScalarField` / `MatrixField` | **No** | — |
| `qd.field` / `ScalarField` / `MatrixField` at a kernel-read path | **No** | — |
| Anything else at a kernel-read path | **No** | — |

If any parameter is of an unsupported type, fastcache is disabled for that call and the kernel falls back to normal compilation. For `qd.field` / `ScalarField` / `MatrixField` arriving through a `qd.Tensor`-annotated parameter, this is silent — no warning is emitted. For other unsupported types, a warning is logged at the `warn` level identifying the offending parameter.
If any kernel-used parameter is of an unsupported type, fastcache is disabled for that call and the kernel falls back to normal compilation. For `qd.field` / `ScalarField` / `MatrixField` arriving through a `qd.Tensor`-annotated parameter, this is silent — no warning is emitted. For other unsupported types, a warning is logged at the `warn` level identifying the offending parameter.

Kernel-unused members of any type — including unrecognised ones — do **not** disable fastcache. Fastcache skips them entirely, so opaque metadata (UUIDs, Pydantic configs, parent back-pointers) attached to a `@qd.data_oriented` or `dataclasses.dataclass` instance is harmless as long as the kernel doesn't read it.

### 3. Source code must be available

Expand All @@ -120,6 +123,12 @@ Each compiled artifact is stored under a key derived from all of the following:

When any of these change, the resulting key is different, so a new compilation occurs and a new entry is stored. Previous entries remain on disk — multiple cached versions coexist. You do not need to manually clear the cache when making code changes — the hash mismatch causes a transparent recompilation.

### Two strict invariants

1. **If the kernel does not read or write a variable, it is entirely ignored by fastcache.** It will not cause fastcache to fail, nor emit a warning, nor emit an error.

2. **Unrecognised types at variables the kernel reads or writes must not be silently dropped or hashed by type-name.** If the value of such a variable has a type fastcache doesn't explicitly handle (Pydantic models, UUIDs, third-party tensor wrappers, …), fastcache is disabled for the call with a one-shot `[FASTCACHE][UNKNOWN_TYPE]` warning identifying the offending type plus an `[INVALID_FUNC]` log line confirming the cache is off.

## Advanced

### Diagnostics
Expand All @@ -143,32 +152,25 @@ print(obs.cache_stored) # True if the compiled kernel was stored to cach

On the first run you'll see `cache_stored=True` but `cache_loaded=False`. On the second run (after `qd.init`), `cache_loaded=True`.

## Appendix

### Compound-type cache keying

The args hasher walks compound-type kernel parameters recursively. For each leaf member it decides what (if anything) contributes to the cache key. The headline rules:
For `@qd.data_oriented` and `dataclasses.dataclass` kernel parameters, fastcache walks members recursively. Any members that are not themselves read or written by the kernel, nor contain members read or written by the kernel, are skipped during the walk (per the [strict invariants](#two-strict-invariants) above). Member-by-member behavior:

@duburcqa duburcqa Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is redundant with:

  1. Unrecognised types at variables the kernel reads or writes must not be silently dropped or hashed by type-name. If the value of such a variable has a type fastcache doesn't explicitly handle (Pydantic models, UUIDs, third-party tensor wrappers, …), fastcache is disabled for the call with a one-shot [FASTCACHE][UNKNOWN_TYPE] warning identifying the offending type plus an [INVALID_FUNC] log line confirming the cache is off.


**`@qd.data_oriented`:** the walker descends into `vars(obj)`. For each child:
- **`qd.ndarray` member** — `(dtype, ndim, layout)` is included in the cache key. Element values are not.
- **Primitive (`int` / `float` / `bool` / `enum.Enum`) member.** The handling depends on the enclosing container:
- In a `@qd.data_oriented` instance — value is baked into the kernel, same as a `qd.Template` primitive. Two instances of the same class with different primitive member values get different cache entries.
- In a `dataclasses.dataclass` instance — only the type is included by default. To include the value too, annotate the field with `FIELD_METADATA_CACHE_VALUE`:

- `qd.ndarray` member — `(dtype, ndim, layout)` is included in the cache key. Element values are not.
- Primitive (`int` / `float` / `bool` / `enum.Enum`) member — value is baked into the kernel (same semantics as a `qd.Template` primitive). Two instances of the same class with different primitive member values get different cache entries.
- Nested `@qd.data_oriented` member — recurses.
- Nested `dataclasses.dataclass` member — recurses (with the dataclass rules below).
- `qd.field` member — fastcache is disabled for the entire kernel call. The kernel still runs via normal compilation; a warn-level log line is emitted.

**`dataclasses.dataclass`:** the walker descends into the declared members. For each member, only the *type* is included in the cache key by default — **not** the value. To include a member's value, annotate it:

```python
import dataclasses
from quadrants.lang._fast_caching import FIELD_METADATA_CACHE_VALUE

@dataclasses.dataclass
class SimConfig:
num_layers: int = dataclasses.field(metadata={FIELD_METADATA_CACHE_VALUE: True})
dt: float = dataclasses.field(metadata={FIELD_METADATA_CACHE_VALUE: True})
```
```python
import dataclasses
from quadrants.lang._fast_caching import FIELD_METADATA_CACHE_VALUE

This is necessary whenever the compiled kernel depends on the member's *value* rather than just its type (for example, when the value is used as a loop bound that the compiler bakes into the generated code). Without the annotation, two `SimConfig` instances with different `num_layers` values would share a fastcache key, and the second instance would silently load a kernel compiled for the wrong value.
@dataclasses.dataclass
class SimConfig:
num_layers: int = dataclasses.field(metadata={FIELD_METADATA_CACHE_VALUE: True})
dt: float = dataclasses.field(metadata={FIELD_METADATA_CACHE_VALUE: True})
```

Note the asymmetry: `@qd.data_oriented` primitive members are baked into the kernel automatically (same semantics as `qd.Template`); `dataclasses.dataclass` members contribute only their *type* to the cache key unless you opt in per-member.
Annotate any member whose *value* (not just type) affects the compiled kernel. Primarily this means any variable used inside [`qd.static`](static.md).
- **Nested `@qd.data_oriented` or `dataclasses.dataclass` member** — recurses with the same rules (so an `int` inside a nested `@qd.data_oriented` is still baked into the kernel; an `int` inside a nested `dataclasses.dataclass` still needs `FIELD_METADATA_CACHE_VALUE` to bake its value).
- **`qd.field` member** — fastcache is disabled for the entire kernel call. The kernel still runs via normal compilation; a warn-level log line is emitted.
Loading
Loading