Skip to content

Give the workspaces of sorts and scans along dims a concrete type - #171

Merged
maleadt merged 4 commits into
JuliaGPU:mainfrom
AzamatB:ab/infer-merge-sort-workspace
Oct 27, 2026
Merged

maleadt merged 4 commits into
JuliaGPU:mainfrom
AzamatB:ab/infer-merge-sort-workspace

Conversation

@AzamatB

@AzamatB AzamatB commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #170.

The Workspace that AK.workspace returned for merge sort's operations (sort!, sortperm!, sortperm, sort_by_key!), for RadixSort, and for accumulate along dims had an abstract type. A caller that kept its scratch in a workspace then dispatched at run time every time it used it: the call that took it (sortperm!(...; workspace=ws)), and any use of its buffers.

The plans listed different buffers depending on the length:

  • merge sort: none for one element, swap buffers only where tiles are merged, and for sortperm! different buffers with lowmem;
  • radix sort: none where the slices sort in one block;
  • SliceScan: chunk totals only where a slice splits into chunks.

Inference widened these unions.

Each of these plans now lists the same buffers (and nested plans) for every length, without elements where the call doesn't need them (scratch(T, dims; needed), no_elements). The whole-array reductions already did this with their partials. The low-memory sortperm! path lists the same buffers as the default one, with empty keys, and swaps the indices through temp_values. The kernels touch the same memory as before, and each call allocates the same device memory.

With Auto(), sortperm! and sort_by_key! also inferred their algorithm as a union of the three kernel sorts, even though select_sort always picks MergeSort for them. resolve_sort is now constant-propagated aggressively so that perm and pairs reach it as constants.

Every merge sort, radix sort and accumulate(; dims) workspace now infers concretely, including sortperm! with a run-time lowmem::Bool. @report_opt reports nothing for the example in #170.

Costs:

  • A workspace also fixes the element types and the number of dimensions of the buffers a call doesn't use. For example, a low-memory sortperm! workspace is now tied to the key type.
  • A call without a workspace on a short input allocates a few zero-length arrays.

Not covered:

@AzamatB

AzamatB commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

How can I get this merged?

@maleadt

maleadt commented Oct 26, 2026

Copy link
Copy Markdown
Member

I'll rebase this.

@maleadt maleadt self-assigned this Oct 26, 2026
@maleadt
maleadt force-pushed the ab/infer-merge-sort-workspace branch from e05dcbd to 7aae07e Compare October 26, 2026 08:25
@maleadt maleadt changed the title Give merge sort's workspaces a concrete type Give the workspaces of sorts and scans along dims a concrete type Oct 26, 2026
@maleadt

maleadt commented Oct 26, 2026

Copy link
Copy Markdown
Member

Rebased and slightly reworked.

AzamatB and others added 4 commits October 26, 2026 10:56
Merge sort's plans listed different buffers depending on the length (none
for an input of one element, swap buffers only where tiles are merged) and,
for `sortperm!`, on `lowmem`. Inference widens these unions, so the
workspaces of `sort!`, `sortperm!`, `sortperm` and `sort_by_key!` had
abstract types, and a caller that passed one to the operation, or used its
buffers, dispatched at run time.

Merge sort now lists the same buffers for every length, without elements
where the call does not need them, as the whole-array reductions already do
with their partials. The low-memory `sortperm!` path lists the same buffers
as the default one, with empty keys, and swaps the indices through
`temp_values`, so that the plan does not depend on `lowmem` either; only
keys of a union type, which some backends cannot allocate even empty, are
left out. Zero-dimensional arrays, which have no empty buffers of their
shape, keep needing none. The device memory each call allocates is
unchanged.
`select_sort` resolves `Auto()` to `MergeSort()` for a permutation or a
key/value sort on a GPU, but `perm` and `pairs` did not reach it as
constants: the algorithm inferred as the union of the three kernel
algorithms, and so did the plans and workspaces of `sortperm!` and
`sort_by_key!`. Constant-propagating `resolve_sort` aggressively lets them
infer as merge sort's (or, on the host, as `CPUThreads.SampleSort`'s).
Radix sort's plan listed no buffers for slices of at most one element or
slices sorted in one block, and its buffers and nested plans otherwise, so
`workspace(sort!, v; alg=RadixSort())` inferred as an abstract `Workspace`.
It now lists the same buffers and nested plans for every length, without
elements where the call does not need them, with `no_elements` emptying a
whole plan.
`SliceScan` listed its chunk totals only where a slice splits into chunks,
so the plans and workspaces of `accumulate` along `dims` inferred as
abstract. It now lists them for every shape, without elements for slices
that fit in one chunk.
@maleadt
maleadt force-pushed the ab/infer-merge-sort-workspace branch from 7aae07e to 6f38584 Compare October 26, 2026 10:06
@maleadt
maleadt merged commit 4ba2565 into JuliaGPU:main Oct 27, 2026
34 of 35 checks passed
@AzamatB
AzamatB deleted the ab/infer-merge-sort-workspace branch October 28, 2026 03:12
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.

Merge sort's workspaces have abstract types, so callers dispatch at run time

2 participants