Repository navigation
perf(cpu-ep): partial-select TopK instead of full axis sort - #775
Merged
Merged
Conversation
The CPU TopK kernel fully sorted every lane's entire axis (O(width log width)) then truncated to k, even when k << width. For a 152k-wide vocab axis with k=40 that is ~17x more comparisons than necessary. Replace the full sort with select_nth_unstable_by (introselect, O(width) average) to partition the k winners to the front, then sort only those k in O(k log k) -- the same partial-selection technique the sampler's top_k_threshold already uses. Output layout, index tie-breaking, and both largest/smallest orders are unchanged. Add tests: partial-select tie-break by index (k<width duplicates) and largest=false partial-select. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
justinchuby
enabled auto-merge (squash)
August 11, 2026 15:14
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #775 +/- ##
==========================================
+ Coverage 79.89% 80.22% +0.33%
==========================================
Files 331 331
Lines 137491 137527 +36
Branches 137491 137527 +36
==========================================
+ Hits 109848 110335 +487
+ Misses 23299 22848 -451
Partials 4344 4344
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
🔴 Benchmark Regression DetectedComparison of criterion micro-benchmarks: PR head vs merge-base, measured on the same runner in the same job (base first → PR second).
Visual flags: Host infoWhat this cannot catch
|
justinchuby
added a commit
that referenced
this pull request
Aug 11, 2026
Bookkeeping: merge 37 decision-inbox drops into decisions.md (detail archived to decisions-archive/2026-08.md), add standing entries for #774 (TopK k-major fix), #775 (partial-select TopK perf), #779 (27B hybrid-GDN native enablement). No code changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
justinchuby
added a commit
that referenced
this pull request
Aug 12, 2026
…detail (#825) Justin asked to update `docs/PROGRESS.md` with the latest progress and drop stale info. It had grown to **1166 lines** of day-by-day changelog spanning 2026-07-15 → 2026-07-27, with the entire recent arc missing. ## Changes (1166 → 303 lines) - **Added recent milestones (2026-07-28 → 2026-08-12), newest-first:** - EP plugins run inside ORT via the plugin-EP C ABI (#762) and ship on PyPI — **`nxrt-ep-cpu` + `nxrt-ep-cuda` 0.1.0.dev5 both LIVE** (#819/#824/#820). - Absent-optional-output memory-safety wave (#762 review; Miri-clean). - VMM / KV-layout / offload / batching residency wave — managed no-spill VMM default, layout-governed KV floor (768× reduction), offload+capture coexist, #736 over-reservation audit (#755–#814). - Issue triage + autonomous fixes (#784/#785/#786/#788/#789); CPU-EP TopK correctness+perf (#774/#775). - Qwen3.5/3.6-27B hybrid Gated-DeltaNet native CUDA (#779); GLM-4-9B + DeepSeek-V2-Lite native + ORT 1.28 3-config fairness (#766/#770/#771). - Fused QMoE decode kernel + QMoE arc concluded; CUDA-graph capture trilogy, version-selectable CUDA (#708/#728/#757/#760), megakernel study (#769). - **Compressed stale detail:** the ~40 dated July day-by-day sections + verbose diffusion/LoRA/ORT2 narrative collapsed into a ~35-line "Foundational milestones" spine; superseded perf-wave numbers dropped; §1–§40 table condensed to a compact grid. - **Header refreshed:** last-updated → 2026-08-12; Published line now lists both EP plugins on PyPI + Miri CI. - **Preserved & updated** the DeepSeek/GLM known-gaps checklists and added new open items. Docs-only; full narrative remains in `.squad/decisions-archive/` and `docs/benchmarks/`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Motivation
@justinchuby noticed the CPU-EP
TopKKernelisn't vectorized and does a full sort of every lane's entire axis (O(width·log width)) then truncates to k — even whenk << width. Meanwhile the sampler (logits.rs::top_k_threshold) already uses an efficient bounded-heap /select_nth_unstable_by(introselect) approach. This aligns the kernel with that faster technique.Change
Replace the per-lane full sort with:
select_nth_unstable_by(k-1, …)— introselect partition, O(width) average, moves the k winners to the front.sort_byon just the k winners — O(k·log k).For a 152k-wide vocab axis with k=40 this is ~an order of magnitude fewer comparisons.
topk_orderis a total order (ties broken by index), so the partition is deterministic; output layout (#774 k-major), index tie-breaking, and bothlargest/smallestorders are unchanged.Tests
topk_partial_select_breaks_ties_by_index(k<width with duplicate 5.0s → must pick lowest indices [0,1,2]).topk_smallest_partial_select(largest=false, ascending partition).cargo test -p onnx-runtime-ep-cpu --lib selection: 24 passed / 0 failed. fmt + clippy clean.Generality: no model-name/shape special-casing. Builds on #774.