Decouple thread fusion from the Read/Write Operation authoring contract - #324
Open
johnnynunez wants to merge 2 commits into
Open
Decouple thread fusion from the Read/Write Operation authoring contract#324johnnynunez wants to merge 2 commits into
johnnynunez wants to merge 2 commits into
Conversation
Operations now only implement their scalar exec(). The vectorized multi-element machinery lives in one central place: ThreadFusionAdapter in core/execution_model/thread_fusion.h synthesizes the wide load/store and the per-element packing/unpacking, driven by two declarative opt-in hooks on the Operation: - contiguous_data(params): plain pitch-linear accesses (PerThreadRead/PerThreadWrite/TensorRead/TensorWrite) - forwarded_access(thread, params): thread-remapping wrappers that delegate the access to a wrapped Operation (BatchRead/BatchWrite, CircularBatchRead/Write, CircularTensorRead/Write) An Operation must declare exactly one of the two hooks; declaring both is rejected at compile time by ThreadFusionAdapter. contiguous_data on a Write Operation additionally requires InputType == WriteDataType (the wide store is computed from InputType), enforced by a static_assert. The TF template parameter and the THREAD_FUSION member are removed from ReadOperation/WriteOperation parents and the DECLARE_*_PARENT macros; capability is now derived from the hooks (isThreadFusionCapable), defaulting to disabled with zero boilerplate for Operations that do not care. The macro-generated exec(thread, opData) dispatchers are constrained with enable_if instead of a static_assert in the body, so they stay SFINAE-friendly for detection traits while the body remains lazily checked (Operations may implement a non-constexpr scalar exec()). Public API is unchanged: executeOperations and TransformDPP<PA, TF::ENABLED/DISABLED> behave identically, and the vectorized codegen for the ops that had it is preserved (SASS global load/store mix is identical on sm_121). isThreadDivisible now passes the IOps directly to num_elems_x instead of re-constructing an OperationData from .params, which also makes TF::ENABLED compile for BatchWrite (array ParamsType), and it checks the row width of every z plane instead of only plane 0, because batch Operations can hold a different width per plane; any non-divisible plane selects the remainder-path kernel variant instead of the wide-access one. Adds tests/thread_fusion/test_thread_fusion_exec.h, which executes every pipeline with TF::DISABLED and TF::ENABLED on both backends and compares the outputs: contiguous ops, type-changing pipelines, non divisible widths (remainder path), heterogeneous per-plane batch widths, Tensor ops, and the batch/circular wrappers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
albertandaluz
force-pushed
the
feat/decouple-thread-fusion
branch
from
August 27, 2026 15:28
215e888 to
2889fad
Compare
albertandaluz
force-pushed
the
feat/decouple-thread-fusion
branch
from
August 27, 2026 15:29
2889fad to
6221983
Compare
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.
What
Memory Operations no longer implement
template <uint ELEMS_PER_THREAD> exec(...)overloads. Every Operation authors only its scalarexec(), and the vectorized multi-element machinery lives in exactly one place:ThreadFusionAdapterincore/execution_model/thread_fusion.hsynthesizes the wide load/store and per-element packing/unpacking.Thread-fusion capability is now a declarative opt-in on the Operation, defaulting to disabled:
contiguous_data(params)— for Operations whoseexec()is a plain per-thread access into a pitch-linearRawPtr(PerThreadRead/PerThreadWrite/TensorRead/TensorWrite: one-line identity accessor each).forwarded_access(thread, params) -> ForwardedAccess<Operation>— for wrapper Operations that only remap the thread and/or select per-plane data and delegate the access to a wrapped Operation (BatchRead/BatchWrite,CircularBatchRead/Write,CircularTensorRead/Write). Their scalarexec()is now written in terms of the same hook, removing the duplicatedif constexpr (THREAD_FUSION)dispatch blocks.ReadOperation/WriteOperationparents and theDECLARE_READ/WRITE_PARENTmacros no longer take anenum TFparameter nor defineTHREAD_FUSION; capability is derived viaisThreadFusionCapable<Op>(hook detection, recursing through forwarded targets).ELEMS_PER_THREADnow appears only inthread_fusion.h.Why
TFtemplate argument,THREAD_FUSIONpropagation, templated exec signatures usingThreadFusionType) even for ops that never vectorize. Six ops opted out withTF::DISABLEDpurely to avoid that machinery.ELEMS_PER_THREADmentions; a single adapter removes the duplication and makes the vectorized path testable and reviewable in one place.enable_if-constrained on the operation-data type, so they stay SFINAE-friendly for detection traits while their bodies remain lazily checked — Operations may still implement a non-constexpr scalarexec()(e.g.Fp8TokenDequantRead).Behavior
executeOperations<...>,TransformDPP<PA, TF::ENABLED/DISABLED>and all existing call sites compile and behave identically. TF is still silently disabled for ReadBack chains, fused reads, and non-capable endpoints.LDG.E.128, 73xSTG.E.128); no kernel grew, 119/151 kernels got 8–32 instructions smaller (mostly the non-divisible remainder variants). Benchmark TF-vs-normal geomean speedups are unchanged (1.067→1.059, 1.024→1.025, 1.010→1.011).isThreadDivisiblenow passes the IOps directly tonum_elems_xinstead of re-constructing anOperationDatafrom.params. This is behavior-identical and additionally makesTF::ENABLEDcompile forBatchWrite(arrayParamsType), so TF now works for batch read→write pipelines.ReadOperation<..., TF::X, Child>/WriteOperation<..., TF::X, Child>must drop theTFargument (loud compile error, one-token fix). All in-repo instantiations updated.Tests
tests/thread_fusion/test_thread_fusion_exec.h: executes each pipeline withTF::DISABLEDandTF::ENABLEDon both backends and compares outputs — contiguous ops (uchar/uchar2/uchar3/float/float4), non-divisible widths (remainder path), type-changing pipelines, Tensor ops, and the batch/circular wrappers. Comments document the pre-existing constraint that Tensor rows are unpadded, so TF on Tensors needs divisible widths.benchmark_thread_fusionpasses on both backends (its compareAndCheck validates TF vs non-TF equality across all supported element types).Docs
.github/skills/fkl-implementing-operations/SKILL.md: new section describing the two hooks and stating thatELEMS_PER_THREADoverloads must never be hand-written.CLAUDE.md: thread-fusion bullet updated to the new mechanism.🤖 Generated with Claude Code