You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`StaticDispatch` is a developer-maintained static dispatch table that maps shape conditions directly to pre-determined kernel factories. **No autotune, no benchmarking, no caching** — conditions are evaluated in order, and the first match wins immediately.
6
6
7
+
The dispatch table is designed to be created once at module level and reused across calls. Per-call varying data (tensors, scalars) is passed through a `context` dict, so factories don't need to capture variables via closures.
A callable that determines whether the current shape matches this table entry. Conditions are evaluated **in table order**; the first that returns `True` wins.
A zero-arg callable that returns a **runner**. The runner is itself a zero-arg callable that executes the kernel.
35
+
A named function (recommended over lambdas) that accepts per-call varying arguments via keyword arguments and returns a **runner** — a zero-arg callable that executes the kernel. The arguments are passed from the `context` dict in `lookup_and_build()`.
34
36
35
-
**Critical**: When used with `@libentry()`-decorated Triton kernels, the factory must use a **double-lambda** wrapper:
37
+
When the dispatch table is created at module level, factories are pure references to named functions — no closures, no lambdas rebuilt on every call. The per-call data flows in through `context`.
36
38
37
-
```python
38
-
# Correct: double lambda
39
-
lambda: lambda: kernel_fn[grid](arg1, arg2, ...)
40
-
# ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
41
-
# factory runner (deferred to runner())
39
+
**Critical**: When used with `@libentry()`-decorated Triton kernels, the factory must return the kernel call wrapped in a **lambda**:
42
40
43
-
# Wrong: single lambda — kernel executes immediately in factory()
44
-
lambda: kernel_fn[grid](arg1, arg2, ...)
41
+
```python
42
+
# Module level — defined once:
43
+
defbuild_my_kernel(A, B, C, alpha, beta, m, n, k, lda, ldb, ldc):
**Why**: `kernel_fn[grid](args...)` calls `LibEntry.run()`, which launches the kernel immediately and returns a `(kernel, constexprs)` tuple — not a callable runner. The extra`lambda:` defers execution to `runner()` time.
61
+
**Why lambda**: `kernel_fn[grid](args...)` calls `LibEntry.run()`, which launches the kernel immediately and returns a `(kernel, constexprs)` tuple — not a callable runner. The `lambda:` defers execution to `runner()` time.
runner = dispatch.lookup_and_build(m, n, k, aligned, **extra)
100
+
runner = dispatch.lookup_and_build(m, n, k, aligned, *, context=None, **extra)
86
101
runner()
87
102
```
88
103
@@ -92,6 +107,7 @@ runner()
92
107
|`n`|`int`| N dimension |
93
108
|`k`|`int`| K dimension |
94
109
|`aligned`|`bool`| Whether inputs are memory-aligned |
110
+
|`context`|`dict` or `None`| Per-call varying data (tensors, scalars, etc.) passed as keyword arguments to the matched factory. When `None`, factories are called with no arguments. |
95
111
|`**extra`| — | Additional keyword arguments passed to each condition |
96
112
97
113
**Returns**: `Callable[[], None]` — zero-arg runner; calling it executes the selected kernel.
@@ -102,82 +118,119 @@ runner()
102
118
103
119
## Real-World Example: hgemm NN
104
120
105
-
This example comes from [hopper/ops/gemm.py](../src/flag_blas/runtime/backend/_nvidia/hopper/ops/gemm.py), the `hgemm` function's NN branch:
121
+
This example comes from [hopper/ops/gemm.py](../src/flag_blas/runtime/backend/_nvidia/hopper/ops/gemm.py), the `hgemm` function's NN branch.
122
+
123
+
### Module level — defined once
106
124
107
125
```python
108
126
from flag_blas.runtime.dispatch import StaticDispatch
109
127
from triton.tools.tensor_descriptor import TensorDescriptor
2.**Ordered matching**: Conditions evaluated top-to-bottom; first `True` wins.
207
260
3.**Catch-all required**: The last entry must match any shape (prevent `ValueError`).
208
261
4.**Mutually exclusive conditions**: Entries should not overlap to make behavior predictable. Higher-priority entries should have more specific conditions.
209
-
5.**Double-lambda factories**: Required when using `@libentry()`-decorated Triton kernels. The inner `lambda:` defers `LibEntry.run()`to `runner()` time.
262
+
5.**Named functions, not lambdas in the table**: Conditions and factories should be module-level named functions referenced by name in the `StaticDispatch` table. Per-call varying data (tensors, scalars) is passed via the `context` dict to `lookup_and_build()`. This avoids recreating closures on every call.
210
263
211
264
---
212
265
@@ -241,37 +294,68 @@ runner()
241
294
242
295
### Common Patterns
243
296
244
-
**Priority by dimension**:
297
+
**Priority by dimension** (module-level named functions):
245
298
```python
246
-
[
247
-
(lambdam, *_kw, **__: m >8192, factory_a), # very large
248
-
(lambdam, *_kw, **__: m >1024, factory_b), # large
249
-
(lambdam, *_kw, **__: m >256, factory_c), # medium
250
-
(lambda**_kw: True, factory_d), # small
251
-
]
299
+
defis_very_large(m, **_kw):
300
+
return m >8192
301
+
302
+
defis_large(m, **_kw):
303
+
return m >1024
304
+
305
+
defis_medium(m, **_kw):
306
+
return m >256
307
+
308
+
defis_default(**_kw):
309
+
returnTrue
310
+
311
+
_DISPATCH= StaticDispatch([
312
+
(is_very_large, build_kernel_a),
313
+
(is_large, build_kernel_b),
314
+
(is_medium, build_kernel_c),
315
+
(is_default, build_kernel_d),
316
+
])
252
317
```
253
318
254
319
**Priority by alignment**:
255
320
```python
256
-
[
257
-
(lambdaaligned, **_kw: aligned and is_large(**kw), aligned_large_factory),
Yes. For example, use `StaticDispatch` for well-understood shape classes and `SizeAutoDispatch` for the remainder. Just ensure you return the runner from the appropriate dispatch.
310
394
311
-
### Q: Why double-lambda for @libentry kernels?
395
+
### Q: Why return a lambda from the factory?
312
396
313
-
`@libentry()` wraps a Triton `JITFunction` in a `LibEntry` object. Calling `entry[grid](args...)` triggers `LibEntry.run()`, which compiles (if needed), launches the kernel, and returns `(kernel_obj, constexprs)`. The inner`lambda:` wraps this into a callable runner without executing the kernel.
397
+
`@libentry()` wraps a Triton `JITFunction` in a `LibEntry` object. Calling `entry[grid](args...)` triggers `LibEntry.run()`, which compiles (if needed), launches the kernel, and returns `(kernel_obj, constexprs)`. The returned`lambda:` wraps this into a callable runner without executing the kernel immediately.
0 commit comments