You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue can be closed by profiling the host overhead for reduce, scan, segmented reduce, and segmented sort, and filing follow on issues for optimizations.
Independent of any library change, callers can already avoid a large chunk of per-call overhead themselves:
Don't query temp-storage size on every call. The size only depends on shape/dtype, not on the call — query it once and reuse the buffer.
If your array sizes vary and you don't want to size for the worst case up front, track the largest size seen so far and only re-query when you see something bigger. (Query cost doesn't shrink for smaller sizes than what you've already sized for.)
Don't share one built algorithm object across threads without synchronization. These objects mutate shared state on every call; concurrent, unsynchronized calls into the same object can race.
I think this PR needs to retain its original focus of doing work to profile host overhead: create scripts, collect and analyze data, create issues based on findings.
I used cupyx.profiler.time_range context manager to insert NVTX ranges around steps of using cuda.compute. I did this at the script driving algorithm invokations, but also inside a method reimplementing steps performed in the __call__ method of each algorithm that was used to monkey patch the original implementation. I then used nsys to collect profiling data. Here is what I see for reduce_into for reduction of 2048 int32-valued vector (the size is deliberately chosen small to emphasize the overhead) with single level of NVTX ranges. The workspace query takes 7.86 us, allocation of temp-storage takes 4.5 us, and call to enqueue actual computation takes 16.11 us.
When I insert NVTX ranges at higher granularity, that introduces more overhead, but shows that only a small portion of workspace query overhead is actually spent in the call to CUB.
These timelines do show significant time spent in prepare_initial_value step. Ashwin has a PR 12704 to allow user to avoid that step when appropriate (such as when initial value is known to developer to have the same value for all calls), or to avoid messing with state of stateless operators.
Activity
shwina commentedon Sep 21, 2026
First PR addressing host overheads: #11497
shwina commentedon Sep 29, 2026
Independent of any library change, callers can already avoid a large chunk of per-call overhead themselves:
Follow-on issues for library-side fixes: #11732, #11733, #11734.
shwina commentedon Oct 6, 2026
xref: #11895
oleksandr-pavlyk commentedon Oct 28, 2026
I think this PR needs to retain its original focus of doing work to profile host overhead: create scripts, collect and analyze data, create issues based on findings.
Closing this issue does not require sub-issues to be closed (ref: https://github.com/orgs/community/discussions/154148#discussioncomment-13661487)
oleksandr-pavlyk commentedon Nov 10, 2026
I used
cupyx.profiler.time_rangecontext manager to insert NVTX ranges around steps of usingcuda.compute. I did this at the script driving algorithm invokations, but also inside a method reimplementing steps performed in the__call__method of each algorithm that was used to monkey patch the original implementation. I then usednsysto collect profiling data. Here is what I see forreduce_intofor reduction of 2048 int32-valued vector (the size is deliberately chosen small to emphasize the overhead) with single level of NVTX ranges. The workspace query takes 7.86 us, allocation of temp-storage takes 4.5 us, and call to enqueue actual computation takes 16.11 us.When I insert NVTX ranges at higher granularity, that introduces more overhead, but shows that only a small portion of workspace query overhead is actually spent in the call to CUB.
These timelines do show significant time spent in prepare_initial_value step. Ashwin has a PR 12704 to allow user to avoid that step when appropriate (such as when initial value is known to developer to have the same value for all calls), or to avoid messing with state of stateless operators.