Skip to content

Numba: generate Join with named parameters and slice writes - #2362

Open
velochy wants to merge 1 commit into
pymc-devs:mainfrom
velochy:join-dispatch
Open

Numba: generate Join with named parameters and slice writes#2362
velochy wants to merge 1 commit into
pymc-devs:mainfrom
velochy:join-dispatch

Conversation

@velochy

@velochy velochy commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Join's numba implementation was np.concatenate on a *tensors tuple; numba types and lowers an implementation that receives its inputs as one wide tuple quadratically in the input count, and >30-input joins are routine (gradient assembly concatenates one piece per parameter).

The generated implementation takes one named parameter per input and writes each into its slice of a preallocated output — linear codegen, same single copy per input. Split off from #2354 per review there (a dispatch-level Join fix instead of a graph rewrite).

Results

Interleaved with main in one session on an otherwise-idle 1-core box, cold caches (absolute times here are load-sensitive, so only same-session comparisons are quoted), n=80 additive repro whose gradient assembly is a 60-tensor join:

compile peak RSS
main 364.9 s 2696 MB
this PR alone 151.5 s 2660 MB
with #2361 105.9 s 933 MB

The two are complementary: this one removes the wide-tuple callee (np.concatenate typed with a 60-tuple) and carries the compile-time win, while #2361 removes the caller-side bytecode chain and carries the memory win.

post = ", :" * (ndim - ax - 1)
names = [f"x{i}" for i in range(len(node.inputs))]

total_src = "\n".join(f" total += {name}.shape[{ax}]" for name in names[1:])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you use our string codegen helpers? makes it a tiny bit more readable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — rewritten with CODE_TOKEN/build_source_code.


return join
cache_version = 3
return numba_basic.numba_njit(join_fn), cache_version

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

checkbounds=False iff the function already checks shapes are correct

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added boundscheck=False — the slice writes are within the output allocated from the validated shapes by construction.

np.concatenate typed with an n-tuple lowers as O(n^2) LLVM IR; one named
parameter and one slice-write per input into a preallocated output is
linear, with the same single copy per input.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@velochy

velochy commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

You were right about the argument names, so I removed them: the signature is back to *tensors and the body indexes it. I isolated the two changes on top of #2361np.concatenate with a *tensors bundle 325.6 / 323.7 s, slice-writes with the same bundle 107.0 / 107.1 s, slice-writes with named parameters 107.1 / 106.3 s — so the entire 3x is the preallocate-and-slice-write body and the names are worth 0%, exactly as in #2354. Consistent rather than contradictory: np.concatenate is a callee that genuinely consumes the tuple as a tuple, while the slice-write body never touches it as a whole. The shape validation and boundscheck=False stay.

)


@pytest.mark.parametrize("n_terms, axis", [(35, 0), (35, 1), (35, -1)])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

are the new tests relevant now? Sounds like no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants