Repository navigation
Conversation
… and run their kernels as written Operations, checked against NKI's own CPU simulator (nki.simulate_kernel): - nl.where, nl.equal, nl.loop_reduce (dst[...] = op(dst[...], x)), nl.dropout and nl.random_seed (zeroes without rescaling, like NKI), and nl.all_reduce over a grid of one program (several programs raise NotImplementedError: programs run one at a time). - nisa.bn_stats / bn_aggr (even/odd statistics, masked lanes excluded), tensor_partition_reduce, select_reduce (with the Vector Engine accumulator carried across calls), tensor_copy_predicated, and the HBM DMA instructions _tiled_offloaded_memcpy / _tiled_offloaded_fma (traced as loads and stores). - nl.abs as a tensor_scalar operator; get_nc_version follows NEURON_PLATFORM_TARGET_OVERRIDE=trn2 (gen3), as NKI's simulator does. Kernels as the samples write them: - tilelens.trace(frontend="nki") accepts an @nki.jit (or @nki.baremetal) kernel, and the NKI kernels it calls by name (@nki.jit helpers, also imported from other modules) are rewritten too, one rewritten namespace per module. - `a = t[idx]; nl.load(a, mask=m)`: subscripting an HBM tensor gives an access pattern that the load or store transfers with its mask (kernel arguments are HBM tensors). - `t.reshape(shape=...)`, `t.view(dtype)`, a [P] vector indexed as `v[:, 0]`, a predicate subscript `t[mask]`, tensors passed by keyword, [P] / [P, 1] operands of activation, and ncc.multi_buffer directives. - ISA instructions with a destination or reduce_res write through to the tensor an indexed slice came from. Fixes: - A client whose pre_run_callback declines a program (the profiler's block sampling) now skips that program instead of ending the grid (legacy and beta2 NKI runners). - masked_load's fast path no longer fails on float tensors (np.iinfo), and masked_store no longer builds an index grid of the whole tensor; both have a fast path for in-bounds accesses. - The tracer no longer reads numpy masks and keys through `.data` (a memoryview). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm
…itten - tilelens.trace(frontend="nki_beta2") accepts an `@nki.jit` kernel of the `nki` package, as frontend="nki" does for `neuronxcc.nki` (nki_kernel_function moves to trace.py). - The beta2 runner returns the kernel's outputs (as numpy arrays), and a program a client declines is skipped. - nl.shared_hbm is an HBM buffer; nisa.memset; nl.sequential_range and nl.static_range iterate like affine_range; nisa.tensor_copy accepts the `dtype` that NKI's tracer accepts and the tutorial passes. Test: an `@nki.jit` kernel written like the tutorial (checked by NKI's tracer first) returns its output. Full suite with -m "": 647 passed, 2 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm
Its operands are [K, 2, M] and [K, 2, N]: the two rows of tiles are contracted together, so nc_matmul now contracts every axis but the last (the 2-D case is unchanged). The reference double-row fp8 matmul of the NKI samples runs on Trn2 and matches NKI's simulator. Test: a double-row matmul compared with nki.simulate_kernel on the trn2 target. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm
…rom zero - A tensor placed with ncc.sbuf.mod_alloc or ncc.sbuf.alloc is a view of the NeuronCore's physical SBUF (bytes per partition, zeroed per launch), at the addresses NKI's modulo allocation gives (block index modulo the tile counts, linearized, times the tile size, plus the base). Tensors and logical tiles at one address share their values, and a tile declared in a loop at a fixed address keeps its values across iterations. Logical tiles that outnumber the physical ones (num_free_tiles smaller than the blocks) map their block indices modulo the period (AddressedTiles). nl.par_dim marks the partition dimension. - A PSUM tensor starts from zero, also when placed in a bank an earlier tensor used (its first `+=` of a matmul is that matmul, as in NKI's simulator). PSUM tensors were uninitialized numpy arrays, which held zeros only when fresh pages happened to back them. The allocated SD attention and fused RMSNorm-QKV reference kernels and the attention tutorial's v10 now match NKI's simulator. Tests: a double-buffered kernel and a tile declared in a loop compared with nki.simulate_kernel; tiles at one address share their values (the device semantics; NKI's simulator gives each tensor its own storage); PSUM tensors start from zero when a bank is reused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm
…dynamic loops, helpers - Loads, stores, and DMA with oob_mode.skip: out-of-bounds lanes are not moved, and a tile assigned a skipping load keeps its values there (SkippedLanes). Clients see the access as performed (skipped_access: lanes masked off, keys clipped); in_bounds_lanes computes which lanes of t[keys] are in bounds from zero-stride views. - `scalar(t)` (also imported by name from neuronxcc.nki.typing) gives a tile's value, e.g. the trip count of a dynamic loop. - Plain helper functions that call NKI kernels through their own module's globals reach the rewrites: during a launch the kernels in user modules are swapped for their rewrites, and restored afterwards. nki_kernel_function checks an object's type before its attributes. - `nl.store(t[k], v)` left unrewritten (in a helper) takes its second argument as the value. - NKI tiles start with the partition dimension: an operand of fewer dimensions (a [P, 1] bias, a [P, A] predicate of a [P, A, B] tile) aligns from the left. - A scalar index tile (a 0-d integer array) slices like an int, so writes through the slice reach the tile (numpy treated it as an advanced index and copied). - NDArray.itemsize; tile_shape clips out-of-bounds int keys. With these, flash_paged_attention_varlen runs and matches its test's reference (NKI's simulator cannot run it). Tests: skip-mode loads and a plain helper compared with nki.simulate_kernel; a dynamic trip count; left-aligned operands; a scalar index keeping a view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm
Finding the user modules whose kernels to swap for their rewrites, and the modules that import `scalar` by name, scanned every loaded module on every launch: about 9 ms with torch loaded (2,700 modules). The scans are now cached until more modules are loaded; at launch only the user modules (outside Python's library and the installed packages) are checked for kernels, so kernels defined later are still found. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm
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.
Stacked on #540. With this, the legacy NKI interpreter runs the AWS NKI samples' tutorials, reference kernels, and several Neuron-team kernels as the samples write them (
@nki.jitkernels and their helper kernels), with outputs matching NKI's own CPU simulator (nki.simulate_kernel).Operations (checked against
nki.simulate_kernel)nl.where,nl.equal,nl.loop_reduce(dst[...] = op(dst[...], x)),nl.dropout/nl.random_seed(zeroes without rescaling, like NKI; the random numbers do not match the hardware's, as in NKI's simulator),nl.all_reduceover a grid of one program (several programs raiseNotImplementedError, since programs run one at a time).nisa.bn_stats/bn_aggr(even/odd statistics, masked lanes excluded),tensor_partition_reduce,select_reduce(the Vector Engine accumulator carries across calls:reset_reduce,reduce),tensor_copy_predicated, and the HBM DMA instructions_tiled_offloaded_memcpy/_tiled_offloaded_fma(traced as loads and stores).nisa.nc_matmulwithperf_mode="double_row_gen3"(operands[K, 2, M]and[K, 2, N], both rows contracted).nl.absas atensor_scalaroperator;get_nc_version()followsNEURON_PLATFORM_TARGET_OVERRIDE=trn2(gen3), as NKI's simulator does.Kernels as the samples write them
tilelens.trace(frontend="nki")accepts an@nki.jit/@nki.baremetalkernel. The NKI kernels it calls by name (@nki.jit(mode="trace")helpers, also imported from other modules) are rewritten too, with one rewritten namespace per module so kernels that call each other find the rewrites.a = t[idx]; nl.load(a, mask=m): subscripting an HBM tensor gives an access pattern that the load/store transfers with its mask (kernel arguments are HBM tensors). Before, the subscript read the masked-off out-of-range lanes eagerly and raised.t.reshape(shape=...),t.view(dtype), a[P]vector indexed asv[:, 0], a predicate subscriptt[mask](not numpy's boolean selection), tensors passed by keyword (they were not converted to arrays),[P]/[P, 1]operands ofactivation, andncc.multi_bufferdirectives.reduce_reswrite through to the tensor an indexed slice came from.Fixes
pre_run_callbackdeclines a program (the profiler's block sampling) now skips that program instead of ending the grid, in the legacy and beta2 NKI runners (the Triton frontend already skips). Before, the profiler ran nothing unless the sampled program was the first.masked_load's fast path callednp.iinfoon float tensors, so every masked float load took the slow path, which copies the whole tensor;masked_storebuilt an index grid of the whole (padded) tensor for every masked store (690 MB for a (10, 2242, 1280) output). Both now have a fast path for in-bounds accesses, and the store's slow path indexes zero-stride views..data(a memoryview).Builder.store'svalueannotation isNDArray | None(mypy).Beta2 (
import nki): the matrix-multiplication tutorialtilelens.trace(frontend="nki_beta2")accepts an@nki.jitkernel of thenkipackage too.nl.shared_hbmis an HBM buffer;nisa.memset;nl.sequential_range/nl.static_rangeiterate likeaffine_range;nisa.tensor_copyaccepts thedtypethat NKI's tracer accepts and the tutorial passes.@nki.jitkernel written like the tutorial (checked by NKI's tracer first) returns its output.Tensors placed by address, and the paged-attention kernel
ncc.sbuf.mod_alloc/ncc.sbuf.allocare views of a modeled physical SBUF at the addresses NKI's modulo allocation gives, so tensors and logical tiles at one address share values and a tile declared in a loop keeps its values across iterations (AddressedTileswhen logical tiles outnumber physical ones). PSUM tensors start from zero (they were uninitialized numpy arrays, zero only when fresh pages backed them). The allocated SD attention, the allocated fused RMSNorm-QKV, and the attention tutorial's v10 now match NKI's simulator.oob_mode.skipfor loads, stores, and DMA (out-of-bounds lanes are not moved; a tile assigned a skipping load keeps its values there; clients see the access as performed);scalar(t)(also imported by name) for dynamic loops; plain helpers that call NKI kernels through their module's globals reach the rewrites (kernels in user modules are swapped for their rewrites during a launch); an unrewrittennl.store(t[k], v); operands of fewer dimensions align from the partition dimension; a 0-d index tile slices like an int (numpy copied, losing writes);NDArray.itemsize. With these,flash_paged_attention_varlenruns and matches its test's torch reference (NKI's simulator cannot run it).scalarby name) are cached until more modules are loaded: scanning every module cost about 9 ms per launch with torch loaded.Tests
tests/nki/test_nki_more_ops.py(20 tests): selection, statistics, loop reduction, and HBM DMA kernels compared withnki.simulate_kernel; DMA traced as loads and stores; dropout;all_reduceacross programs reported unsupported; a masked gather through an access pattern; a@nki.jitkernel calling a@nki.jit(mode="trace")helper; the tile idioms above; the trn2 target; a client skipping a program, a double-row matmul on the trn2 target, address placement (a double-buffered kernel vs the simulator; shared addresses; PSUM reuse), skip-mode loads and a plain helper vs the simulator, a dynamic trip count, left-aligned operands, and a 0-d index keeping a view. Full suite with-m "": 655 passed, 2 skipped.🤖 Generated with Claude Code
https://claude.ai/code/session_0169QRxKtsivkLDCS5f7rrsm