Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

116 lines
4.2 KiB
ReStructuredText

.. _libcudacxx-extended-api-work-stealing:
Work stealing
=============
Defined in header ``<cuda/work_stealing>`` if the CUDA compiler supports at least PTX ISA 8.7:
.. code:: cuda
namespace cuda {
template <int ThreadBlockRank = 3, typename UnaryFunction = ..unspecified..>
__device__ void for_each_canceled_block(UnaryFunction uf);
} // namespace cuda
**Note**: On devices with compute capability 10.0 or higher, this function may leverage hardware acceleration.
This API is primarily intended for implementing work-stealing at the thread-block level.
Compared to alternative work distribution techniques, such as `grid-stride loops <https://developer.nvidia.com/blog/cuda-pro-tip-write-flexible-kernels-grid-stride-loops/>`__, which distribute work statically, or dynamic work distribution methods relying on global memory concurrency, this API offers several advantages:
- It enables dynamic work-stealing: thread blocks that complete their tasks sooner can take on additional work from slower thread blocks.
- It may cooperate with the GPU work scheduler to respect work priorities and improve load balancing.
- It may reduce work-stealing latency compared to global memory atomics.
For better performance, extract the shared thread-block prologue and epilogue outside the lambda and reuse them across thread-block iterations:
- Prologue: Thread-block initialization code and data common to all thread blocks, such as ``__shared__`` memory allocation and initialization.
- Epilogue: Epilogue: Thread-block finalization code common to all thread blocks, such as writing shared memory back to global memory..
**Mandates**:
- ``ThreadBlockRank`` equals the rank of the thread block: ``1``, ``2``, or ``3`` for one-dimensional, two-dimensional, and three-dimensional thread blocks, respectively.
- ``is_invokable_r_v<UnaryFunction, void, dim3>`` is true.
**Preconditions**:
- All threads within a thread block shall call ``for_each_canceled_block`` **exactly once**.
**Effects**:
- Invokes ``uf`` with ``blockIdx`` and then repeatedly attempts to cancel the launch of another thread block within the current grid:
- If successful: invokes ``uf`` with the canceled thread block's ``blockIdx`` and repeats.
- Otherwise, the function returns; it failed to cancel the launch of another thread block.
Example
-------
This example demonstrates work-stealing at thread-block granularity using this API.
.. code:: cuda
// Before:
#include <cuda/math>
#include <cuda/functional>
__global__ void vec_add(int* a, int* b, int* c, int n) {
// Extract common prologue outside the lambda, e.g.,
// - __shared__ or global (malloc) memory allocation
// - common initialization code
// - etc.
cuda::for_each_canceled_block<1>([=](dim3 block_idx) {
// block_idx may be different than the built-in blockIdx variable, that is:
// assert(block_idx == blockIdx); // may fail!
// so we need to use "block_idx" consistently inside for_each_canceled:
int idx = threadIdx.x + block_idx.x * blockDim.x;
if (idx < n) {
c[idx] += a[idx] + b[idx];
}
});
// Note: Calling for_each_canceled_block<1> again from this
// thread block exhibits undefined behavior.
// Extract common epilogue outside the lambda, e.g.,
// - write back shared memory to global memory
// - external synchronization
// - global memory deallocation (free)
// - etc.
}
int main() {
int N = 10000;
int *a, *b, *c;
cudaMallocManaged(&a, N * sizeof(int));
cudaMallocManaged(&b, N * sizeof(int));
cudaMallocManaged(&c, N * sizeof(int));
for (int i = 0; i < N; ++i) {
a[i] = i;
b[i] = 1;
c[i] = 0;
}
const int threads_per_block = 256;
const int blocks_per_grid = cuda::ceil_div(N, threads_per_block);
vec_add<<<blocks_per_grid, threads_per_block>>>(a, b, c, N);
cudaDeviceSynchronize();
bool success = true;
for (int i = 0; i < N; ++i) {
if (c[i] != (1 + i)) {
std::cerr << "ERROR " << i << ", " << c[i] << std::endl;
success = false;
}
}
cudaFree(a);
cudaFree(b);
cudaFree(c);
return success? 0 : 1;
}