| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
np.sort already uses Highway VQSort (PR numpy#24018); this patch brings the same acceleration to np.partition via VQSelectStatic. On non-x86 platforms the quickselect_dispatch function now tries Highway VQSelect (covering int16/uint16/int32/uint32/int64/uint64/ float32/float64) before falling back to scalar introselect, matching the existing x86/non-x86 split in quicksort.hpp. A single dispatch file mirrors highway_qsort.dispatch.cpp and is registered for ASIMD and VSX2 targets in meson.build; 16-bit integers ride the same file since they are plain integers covered by base ASIMD (no ASIMDHP target needed). float16 is excluded -- HWY_HAVE_FLOAT16 gating adds complexity and float16 partition is uncommon; scalar introselect is correct there. A minimum-size threshold (HWY_QSELECT_MIN_N = 16384) skips VQSelect on L1-resident arrays where dispatch overhead outweighs the vectorisation gain; the dead DISABLE_HIGHWAY_OPTIMIZATION macro is removed. np.argpartition is not covered; VQSelect is keys-only and cannot maintain a parallel index array. Threshold rationale: a size sweep on M1 Pro (N=64-65536, kth=N/2) showed scalar introselect's random-pivot variance dominates below 16384 -- at N=8192 int64 regresses to 0.28x and float32 to 0.70x. At N=16384 every type reliably wins (1.2x for int64, up to 11x for 16-bit types), so a single threshold is used for all widths. Benchmark: M1 Pro NEON, numpy 2.4.4 scalar vs this branch (ns/elem) dtype n k scalar vqselect speedup ------------------------------------------------- float32 100K 10 5.0 2.3 2.2x float32 1M 100 6.8 2.6 2.6x float32 10M 100 8.9 2.7 3.3x float64 100K 10 8.0 3.6 2.2x float64 1M 100 9.2 4.5 2.0x float64 10M 100 9.3 4.5 2.1x int32 100K 10 6.3 1.3 4.9x int32 1M 100 5.8 1.5 3.8x int64 1M 100 5.9 2.9 2.0x ------------------------------------------------- n < 16K: threshold active, falls back to scalar, parity
The fragment was named after the original (now-closed) PR 31504 on this same branch. Rename to 31506 to match the live PR and follow numpy's upcoming_changes naming convention.
|
@dmatth1 Could you please run bench_function_base.Partition.time_partition benchmarks in the numpy repository and report the changes on you mac? |
Sorry, something went wrong.
|
|
||
| @pytest.mark.skipif( | ||
| not _is_highway_qselect_platform(), | ||
| reason="Highway VQSelect dispatch only active on aarch64/ppc64" |
There was a problem hiding this comment.
Is there a problem running this outside of aarch64/pcc64? If not, no harm adding more test coverage everywhere :)
Sorry, something went wrong.
There was a problem hiding this comment.
Looking over this a bit closer I think test_partition_highway is already run everywhere (same with test_partition_highway_16bit) so pretty sure we can just remove the two _native duplicates
Sorry, something went wrong.
|
Hey, I ran bench_function_base.Partition.time_partition benchmarks on my mac: Ratio = aarch64 with VQSelect / scalar baseline Current ungated Highway VQSelect
Good wins on random but the big regressions come from sorted-block and ordered. We can address these by introducing a gate to fall through to scalar when near-sorted or ordered: Gated VQSelect
The gate introduces a bit of custom logic on the non-x86 path but nothing crazy. Do you prefer to add the gate addressing the ordered and sorted-block regressions? Or are those usecases pretty rare |
Sorry, something went wrong.
|
Thanks, the benchmarks show clear boosts on your platform! @r-devulap has a better idea about the specialized libraries involved, so I'd defer mostly to him, but including VQSelect makes sense to me as long as the performance is consistent across all non-x86 platforms...
The second benchmarks have been run with the gate active, correct? Is this essentially be a single-pass check over the array? I'm not sure if the overhead (and complexity) would be worth it, especially for large arrays (there seems to be a 10-20% overhead when the gate is run), but I may be wrong. Though sorting ordered arrays seems a less common use case at least.1 Footnotes
|
Sorry, something went wrong.
|
Correct the gate I'd propose is a single pass over the array counting the number of descents and only calling VQSelect when not ordered or sorted-block for floats. Does add that 10-20% overhead you called out so definitely a question of whether it's worth it for the added complexity and specificity. Something like // one descent-count pass drives both guards
npy_intp descents = 0;
for (npy_intp i = 1; i < num; i++) descents += (v[i - 1] > v[i]);
// Guard 1 — already-partitioned fast-path
if (descents == 0) { // non-decreasing
bool no_op = true;
if constexpr (std::is_floating_point_v<T>) { // NaN compares false -> re-check
npy_intp nan_count = 0;
for (npy_intp i = 0; i < num; i++) nan_count += (v[i] != v[i]);
no_op = (nan_count == 0);
}
if (no_op) return true; // already partitioned: postcondition met, no work
}
// Guard 2 — dtype-aware disorder gate
bool use_highway = true;
if constexpr (std::is_floating_point_v<T> || sizeof(T) == 8) {
use_highway = (descents > num / 4); // float/64-bit: scalar introselect wins near-sorted
} // 16/32-bit ints skip the gate (VQSelect wins anyway)
if (use_highway) {
#include "highway_qselect.dispatch.h"
NPY_CPU_DISPATCH_CALL_XB(dispfunc = np::highway::qsort_simd::template QSelect, <TF>);
}
Regarding other non-x86 platforms I think the main concern I would have there are some of the constants I've defined, HWY_QSELECT_MIN_N = 16384 and descents > num/4 - not entirely sure that they would hold for Graviton or ppc64/VSX2 (what's covered in the Highway qselect dispatch for numpy). Though near-sorted arrays are perhaps more common than sorted, and I'm not sure if the benchmarks would fare similarly poorly there. Probably worth extending the benchmarks on that front. - Agreed! |
Sorry, something went wrong.
|
I am wondering if this speed difference isn't something to report to highway and maybe fix there? |
Sorry, something went wrong.
+1. I think highway should ideally handle these regressions and fix them inside the library rather than bringing that complexity to NumPy. Perhaps we can explore that option first? |
Sorry, something went wrong.
|
I believe this problem of VQSelect handling near-sorted and sorted poorly is a function of Neon vs x86 SIMD differences (2 lanes vs 8) so I'm not sure how Highway would address it beyond adding the gate I recommended - and that might have a negative downstream impact on other consumers of the library outside of Numpy or those with different data needs. Also makes us wait a little bit for Numpy to vendor the new version of Highway. However if we think Highway should be the ones to address the perf gap I'll open an issue! Note: While working on this PR I discovered a corruption bug with VQSelect and NaN which could prevent us from merging this PR. I just opened an issue with Highway to address it google/highway#3098 so we might need 2 PR's in Highway. |
Sorry, something went wrong.
That means we are missing a test in NumPy. It would be great if you can add that test case here. |
Sorry, something went wrong.
|
one duplicating comment, but maybe you can open an issue about the speed difference at highway as well? Since if they remove any speed difference, then this PR is truly unblocked, I guess. |
Sorry, something went wrong.
|
Sure I can add the test @r-devulap. I opened the issue with Highway about the speed regression (google/highway#3139), will monitor that and see where that leads us |
Sorry, something went wrong.
|
Just a quick bump that this has a conflict now. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PR summary
Uses Highway VQSelect to speed up np.partition for aarch64/Neon/Graviton. Highway VQSort was used for np.sort - see gh-24018 - so we match that pattern here.
A threshold (HWY_QSELECT_MIN_N = 16384) gates when we fallback to scalar, because below this number scalar is faster as the data lives in L1 cache and VQSelect setup dominates.
Benchmarks run on M1 macbook (Neon):
AI Disclosure
Claude used for coding, benchmarks with my review. Built and tests green locally
First-Commit Introduction
Hey, never committed to numpy before but I've been digging into simd-sort acceleration lately and noticed this gap for numpy with my Mac architecture