Repository navigation
Conversation
…terpreter The legacy NKI interpreter failed on the idioms of AWS's NKI samples: - `nl.load(x[...])` / `nl.store(o[...], ...)` nested in another call was not rewritten to the (tensor, keys) form, so the call lost its keys; - `nl.load(t)` and `nl.store(t, value=v)` without an index had no keys at all; - masks broadcast to the tile in NKI (a [128, 1] row mask on a [128, F] tile), but the interpreter and the tracer required the tile's exact shape, and the tracer failed on a load without a mask; - a kernel that allocates its output in shared HBM and returns it returned None, because the runner dropped the kernel's return value; - reductions took a list of axes, which numpy rejects, and dropped the last free axis, where NKI keeps [P, 1]; - max, min, mean, add, subtract, divide, maximum, minimum, static_cast, par_dim, sequential_range, and static_range were not patched, so they ran NKI's real implementation and failed outside an NKI kernel. Load/store keys default to the whole tensor, masks and stored values are broadcast to the tile shape (`tile_shape` / `tile_mask` in masked_load_store.py), the runner returns the kernel's outputs as numpy arrays, and the missing operations are implemented with NKI's semantics (dtype casts, masked reductions, the [P, 1] reduction shape). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Performance Benchmark
Iterations: 1 warmup + 20 measured |
A client whose pre_run or post_run callback returns False stops the remaining programs; the runner then returned None instead of the outputs the programs so far wrote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The legacy NKI interpreter (
frontend="nki") runs only kernels written in a restricted style. Kernels in the style of AWS's NKI samples fail before their first tile operation. Of seven tutorial kernels from the samples (softmax, rmsnorm, two layernorms, transpose2d, average pool 2-D, SPMD tensor addition), only the SPMD addition ran under the tracer on main. The failures come from a few idioms:nl.store(o[i], value=nl.copy(nl.load(x[i]))): a load or store nested in another callNKITransformerrewrotenl.load(x[...])tonl.load(x, keys)only when the call was a whole statement, so the nested call lost its keys:TypeError: missing ... 'keys'nl.load(t),nl.store(t, value=v): whole-tensor accessnl.load(x[i * 128 + ix, iy], mask=(i * 128 + ix < n)): a[128, 1]row mask on a[128, F]tilemasked_loadasserts that the mask has the tile's shapenl.load(x[...])without a maskmask.dataofNoneout = nl.ndarray(..., buffer=nl.shared_hbm)...return outNKIInterpretedFunction.rundropped the kernel's return value, so the launch returnedNonenl.max(t, axis=[1], keepdims=True),nl.sum(t, axis=[1])[128, F]tile gives[128, 1], sot / nl.sum(t, axis=[1])broadcasts in NKI and failed herenl.max,nl.min,nl.mean,nl.add,nl.subtract,nl.divide,nl.maximum,nl.minimum,nl.static_cast,nl.par_dim,nl.sequential_range,nl.static_rangeRuntimeError: calling NKI API outside of NKI kernels is not supportedThis PR:
NKITransformer.visit_Callrewritesnl.load/nl.store/nl.load_transpose2dwith a subscripted first argument wherever the call appears.full_keys), in the interpreter and in the frontend'sLoad/Storeadapters, so clients see the same keys.tile_shape/tile_mask(incore/masked_load_store.py) give the shape ofndarray[keys]even when some lanes are out of bounds, and broadcast an NKI mask to it. The interpreter broadcasts masks and stored values with them; the tracer records the broadcast mask, or an all-true mask for an access without one.returndropped them.Builder._reduce: a list of axes becomes a tuple,maskbecomes numpy'swhere(with the dtype's lowest or highest value as the identity of max / min),dtypecasts the result, and a result that would lose its last free axis gets[P, 1], as NKI's simulator gives. Element-wise operations takedtype;nl.loadtakesdtypetoo.par_dimreturns the size, and the three ranges iterate likerange.The shapes and results were checked against NKI's own CPU simulator (
neuronxcc.nki.simulate_kernel). For example,nl.sumover axis[1]of an[8, 4]tile gives(8, 1); over axes[1, 2]of an[8, 4, 3]tile,(8, 1)withoutkeepdimsand(8, 1, 1)with it. With this PR, six of the seven tutorial kernels run under the tracer. The seventh, layernorm v2, usesnisa.bn_stats, which the interpreter does not implement yet.Tests
tests/nki/test_nki_kernel_idioms.py: four kernels in the samples' style run under TileLens and undernki.simulate_kernel; their outputs must match. The kernels cover a masked softmax over 200 rows, whole-tensor and nested loads and stores, an SPMD addition on a 2×2 grid, and a kernel with loops, casts, and binary operations. All four fail on main. A fifth test stops the grid after the first program with a client whosepost_run_callbackreturns False and checks that the launch still returns that program's output; it fails without the second commit.tests/nki/test_nki_tracer.py::test_tracer_records_reduce_sumnow expects the reduction's output shape(4, 1)instead of(4,), as NKI's simulator gives. Storing that result into the test's 1-D output slice still works: a stored value with the tile's number of elements is reshaped to the tile.pytest tests/ -m ""(NKI tests included): 621 passed, 2 skipped on main; 626 passed, 2 skipped with this PR. Environment: Python 3.12, Triton 3.8.0, neuronx-cc 2.22.12471, nki 0.2.0.ruff checkandruff format --check(v0.1.9) pass on the changed files.🤖 Generated with Claude Code