CCCL (CUDA C++ Core Libraries) provides: - CUB: device/block/warp-level GPU primitives (reduce, scan, sort, topk) - Thrust: high-level parallel algorithms (transform_reduce, sort, scan) - libcudacxx: CUDA C++ standard library (atomics, barriers, memory) - cudax: experimental features (memory resources, allocators) - Tuning policies: per-SM hardware-specific algorithm parameters Competition optimization vectors mapped to CCCL: - Output TPS (83% weight): warp_reduce, block_reduce, device_topk - Input TPS (14% weight): device_scan, block_load, prefetch - Cache TPS (3% weight): prefix caching strategy patterns - Memory (0.9 util): pooled/cached/buddy allocators Source: https://github.com/NVIDIA/cccl (shallow clone, HEAD only) License: Apache-2.0
1315 lines
45 KiB
Plaintext
1315 lines
45 KiB
Plaintext
// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.
|
|
// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.
|
|
// SPDX-License-Identifier: BSD-3
|
|
|
|
//! @file
|
|
//! The cub::BlockExchange class provides :ref:`collective <collective-primitives>` methods for
|
|
//! rearranging data partitioned across a CUDA thread block.
|
|
|
|
#pragma once
|
|
|
|
#include <cub/config.cuh>
|
|
|
|
#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
|
|
# pragma GCC system_header
|
|
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
|
|
# pragma clang system_header
|
|
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
|
|
# pragma system_header
|
|
#endif // no system header
|
|
|
|
#include <cub/detail/uninitialized_copy.cuh>
|
|
#include <cub/util_ptx.cuh>
|
|
#include <cub/util_type.cuh>
|
|
#include <cub/warp/warp_exchange.cuh>
|
|
|
|
#include <cuda/__cmath/pow2.h>
|
|
#include <cuda/__ptx/instructions/get_sreg.h>
|
|
#include <cuda/std/__algorithm/min.h>
|
|
#include <cuda/std/__type_traits/integral_constant.h>
|
|
|
|
CUB_NAMESPACE_BEGIN
|
|
|
|
//! @rst
|
|
//! The BlockExchange class provides :ref:`collective <collective-primitives>` methods for rearranging data partitioned
|
|
//! across a CUDA thread block.
|
|
//!
|
|
//! Overview
|
|
//! +++++++++++++++++++++++++++++++++++++++++++++
|
|
//!
|
|
//! - It is commonplace for blocks of threads to rearrange data items between threads. For example, the
|
|
//! device-accessible memory subsystem prefers access patterns where data items are "striped" across threads (where
|
|
//! consecutive threads access consecutive items), yet most block-wide operations prefer a "blocked" partitioning of
|
|
//! items across threads (where consecutive items belong to a single thread).
|
|
//! - BlockExchange supports the following types of data exchanges:
|
|
//!
|
|
//! - Transposing between :ref:`blocked <flexible-data-arrangement>` and :ref:`striped <flexible-data-arrangement>`
|
|
//! arrangements
|
|
//! - Transposing between :ref:`blocked <flexible-data-arrangement>` and
|
|
//! :ref:`warp-striped <flexible-data-arrangement>` arrangements
|
|
//! - Scattering ranked items to a :ref:`blocked arrangement <flexible-data-arrangement>`
|
|
//! - Scattering ranked items to a :ref:`striped arrangement <flexible-data-arrangement>`
|
|
//!
|
|
//! - @rowmajor
|
|
//!
|
|
//! A Simple Example
|
|
//! +++++++++++++++++++++++++++++++++++++++++++++
|
|
//!
|
|
//! @blockcollective{BlockExchange}
|
|
//!
|
|
//! The code snippet below illustrates the conversion from a "blocked" to a "striped" arrangement of 512 integer items
|
|
//! partitioned across 128 threads where each thread owns 4 items.
|
|
//!
|
|
//! .. code-block:: c++
|
|
//!
|
|
//! #include <cub/cub.cuh> // or equivalently <cub/block/block_exchange.cuh>
|
|
//!
|
|
//! __global__ void ExampleKernel(int *d_data, ...)
|
|
//! {
|
|
//! // Specialize BlockExchange for a 1D block of 128 threads owning 4 integer items each
|
|
//! using BlockExchange = cub::BlockExchange<int, 128, 4>;
|
|
//!
|
|
//! // Allocate shared memory for BlockExchange
|
|
//! __shared__ typename BlockExchange::TempStorage temp_storage;
|
|
//!
|
|
//! // Load a tile of data striped across threads
|
|
//! int thread_data[4];
|
|
//! cub::LoadDirectStriped<128>(threadIdx.x, d_data, thread_data);
|
|
//!
|
|
//! // Collectively exchange data into a blocked arrangement across threads
|
|
//! BlockExchange(temp_storage).StripedToBlocked(thread_data);
|
|
//! }
|
|
//!
|
|
//! Suppose the set of striped input ``thread_data`` across the block of threads is ``{ [0,128,256,384],
|
|
//! [1,129,257,385], ..., [127,255,383,511] }``. The corresponding output ``thread_data`` in those threads will be
|
|
//! ``{ [0,1,2,3], [4,5,6,7], [8,9,10,11], ..., [508,509,510,511] }``.
|
|
//!
|
|
//! Performance Considerations
|
|
//! +++++++++++++++++++++++++++++++++++++++++++++
|
|
//!
|
|
//! - Proper device-specific padding ensures zero bank conflicts for most types.
|
|
//!
|
|
//! Re-using dynamically allocating shared memory
|
|
//! +++++++++++++++++++++++++++++++++++++++++++++
|
|
//!
|
|
//! The ``block/example_block_reduce_dyn_smem.cu`` example illustrates usage of dynamically shared memory with
|
|
//! BlockReduce and how to re-purpose the same memory region. This example can be easily adapted to the storage required
|
|
//! by BlockExchange.
|
|
//! @endrst
|
|
//!
|
|
//! @tparam T
|
|
//! The data type to be exchanged
|
|
//!
|
|
//! @tparam BlockDimX
|
|
//! The thread block length in threads along the X dimension
|
|
//!
|
|
//! @tparam ItemsPerThread
|
|
//! The number of items partitioned onto each thread.
|
|
//!
|
|
//! @tparam WarpTimeSlicing
|
|
//! **[optional]** When `true`, only use enough shared memory for a single warp's worth of
|
|
//! tile data, time-slicing the block-wide exchange over multiple synchronized rounds. Yields a smaller memory footprint
|
|
//! at the expense of decreased parallelism. (Default: false)
|
|
//!
|
|
//! @tparam BlockDimY
|
|
//! **[optional]** The thread block length in threads along the Y dimension (default: 1)
|
|
//!
|
|
//! @tparam BlockDimZ
|
|
//! **[optional]** The thread block length in threads along the Z dimension (default: 1)
|
|
//!
|
|
template <typename T, int BlockDimX, int ItemsPerThread, bool WarpTimeSlicing = false, int BlockDimY = 1, int BlockDimZ = 1>
|
|
class BlockExchange
|
|
{
|
|
static constexpr int BLOCK_THREADS = BlockDimX * BlockDimY * BlockDimZ; ///< The thread block size in threads
|
|
static constexpr int WARP_THREADS = detail::warp_threads;
|
|
static constexpr int WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS; // TODO(bgruber): use ceil_div in
|
|
// C++14
|
|
static constexpr int LOG_SMEM_BANKS = detail::log2_smem_banks;
|
|
|
|
static constexpr int TILE_ITEMS = BLOCK_THREADS * ItemsPerThread;
|
|
static constexpr int TIME_SLICES = WarpTimeSlicing ? WARPS : 1;
|
|
static constexpr int TIME_SLICED_THREADS =
|
|
WarpTimeSlicing ? ::cuda::std::min(BLOCK_THREADS, WARP_THREADS) : BLOCK_THREADS;
|
|
static constexpr int TIME_SLICED_ITEMS = TIME_SLICED_THREADS * ItemsPerThread;
|
|
static constexpr int WARP_TIME_SLICED_THREADS = ::cuda::std::min(BLOCK_THREADS, WARP_THREADS);
|
|
static constexpr int WARP_TIME_SLICED_ITEMS = WARP_TIME_SLICED_THREADS * ItemsPerThread;
|
|
|
|
// Insert padding to avoid bank conflicts during raking when items per thread is a power of two and > 4 (otherwise
|
|
// we can typically use 128b loads)
|
|
static constexpr bool INSERT_PADDING = ItemsPerThread > 4 && ::cuda::is_power_of_two(ItemsPerThread);
|
|
static constexpr int PADDING_ITEMS = INSERT_PADDING ? (TIME_SLICED_ITEMS >> LOG_SMEM_BANKS) : 0;
|
|
|
|
/// Shared memory storage layout type
|
|
struct alignas(16) _TempStorage
|
|
{
|
|
T buff[TIME_SLICED_ITEMS + PADDING_ITEMS];
|
|
};
|
|
|
|
public:
|
|
/// @smemstorage{BlockExchange}
|
|
using TempStorage = Uninitialized<_TempStorage>;
|
|
|
|
private:
|
|
_TempStorage& temp_storage;
|
|
|
|
// TODO(bgruber): can we use signed int here? Only these variables are unsigned:
|
|
unsigned int linear_tid = RowMajorTid(BlockDimX, BlockDimY, BlockDimZ);
|
|
unsigned int lane_id = ::cuda::ptx::get_sreg_laneid();
|
|
unsigned int warp_id = WARPS == 1 ? 0 : linear_tid / WARP_THREADS;
|
|
unsigned int warp_offset = warp_id * WARP_TIME_SLICED_ITEMS;
|
|
|
|
/// Internal storage allocator
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE _TempStorage& PrivateStorage()
|
|
{
|
|
__shared__ _TempStorage private_storage;
|
|
return private_storage;
|
|
}
|
|
|
|
//! @brief Transposes data items from **blocked** arrangement to **striped** arrangement. Specialized for no
|
|
//! timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void BlockedToStriped(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::false_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = linear_tid * ItemsPerThread + i;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * BLOCK_THREADS + linear_tid;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **blocked** arrangement to **striped** arrangement. Specialized for
|
|
//! warp-timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void BlockedToStriped(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::true_type /*time_slicing*/)
|
|
{
|
|
T temp_items[ItemsPerThread];
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int slice = 0; slice < TIME_SLICES; slice++)
|
|
{
|
|
const int slice_offset = slice * TIME_SLICED_ITEMS;
|
|
const int slice_oob = slice_offset + TIME_SLICED_ITEMS;
|
|
|
|
__syncthreads();
|
|
|
|
if (warp_id == slice)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = lane_id * ItemsPerThread + i;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
// Read a strip of items
|
|
const int strip_offset = i * BLOCK_THREADS;
|
|
const int strip_oob = strip_offset + BLOCK_THREADS;
|
|
|
|
if (slice_offset < strip_oob && slice_oob > strip_offset)
|
|
{
|
|
int item_offset = strip_offset + linear_tid - slice_offset;
|
|
if (item_offset >= 0 && item_offset < TIME_SLICED_ITEMS)
|
|
{
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
temp_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
output_items[i] = temp_items[i];
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **blocked** arrangement to **warp-striped** arrangement. Specialized for no
|
|
//! timeslicing
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void BlockedToWarpStriped(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::false_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = warp_offset + i + (lane_id * ItemsPerThread);
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncwarp(0xffffffff);
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = warp_offset + (i * WARP_TIME_SLICED_THREADS) + lane_id;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **blocked** arrangement to **warp-striped** arrangement. Specialized for
|
|
//! warp-timeslicing
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void BlockedToWarpStriped(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::true_type /*time_slicing*/)
|
|
{
|
|
if (warp_id == 0)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i + lane_id * ItemsPerThread;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncwarp(0xffffffff);
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * WARP_TIME_SLICED_THREADS + lane_id;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int slice = 1; slice < TIME_SLICES; ++slice)
|
|
{
|
|
__syncthreads();
|
|
|
|
if (warp_id == slice)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i + lane_id * ItemsPerThread;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncwarp(0xffffffff);
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * WARP_TIME_SLICED_THREADS + lane_id;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **striped** arrangement to **blocked** arrangement. Specialized for no
|
|
//! timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void StripedToBlocked(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::false_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * BLOCK_THREADS + linear_tid;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// No timeslicing
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = linear_tid * ItemsPerThread + i;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **striped** arrangement to **blocked** arrangement. Specialized for
|
|
//! warp-timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void StripedToBlocked(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::true_type /*time_slicing*/)
|
|
{
|
|
// Warp time-slicing
|
|
T temp_items[ItemsPerThread];
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int slice = 0; slice < TIME_SLICES; slice++)
|
|
{
|
|
const int slice_offset = slice * TIME_SLICED_ITEMS;
|
|
const int slice_oob = slice_offset + TIME_SLICED_ITEMS;
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
// Write a strip of items
|
|
const int strip_offset = i * BLOCK_THREADS;
|
|
const int strip_oob = strip_offset + BLOCK_THREADS;
|
|
|
|
if (slice_offset < strip_oob && slice_oob > strip_offset)
|
|
{
|
|
int item_offset = strip_offset + linear_tid - slice_offset;
|
|
if (item_offset >= 0 && item_offset < TIME_SLICED_ITEMS)
|
|
{
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
if (warp_id == slice)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = lane_id * ItemsPerThread + i;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
temp_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
output_items[i] = temp_items[i];
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **warp-striped** arrangement to **blocked** arrangement. Specialized for no
|
|
//! timeslicing
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void WarpStripedToBlocked(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::false_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = warp_offset + (i * WARP_TIME_SLICED_THREADS) + lane_id;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncwarp(0xffffffff);
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = warp_offset + i + (lane_id * ItemsPerThread);
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(output_items + i, temp_storage.buff[item_offset]);
|
|
}
|
|
}
|
|
|
|
//! @brief Transposes data items from **warp-striped** arrangement to **blocked** arrangement. Specialized for
|
|
//! warp-timeslicing
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void WarpStripedToBlocked(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
::cuda::std::true_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int slice = 0; slice < TIME_SLICES; ++slice)
|
|
{
|
|
__syncthreads();
|
|
|
|
if (warp_id == slice)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * WARP_TIME_SLICED_THREADS + lane_id;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncwarp(0xffffffff);
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i + lane_id * ItemsPerThread;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//! @brief Exchanges data items annotated by rank into **blocked** arrangement. Specialized for no timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToBlocked(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
OffsetT (&ranks)[ItemsPerThread],
|
|
::cuda::std::false_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = ranks[i];
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = linear_tid * ItemsPerThread + i;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @brief Exchanges data items annotated by rank into **blocked** arrangement. Specialized for warp-timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToBlocked(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
OffsetT ranks[ItemsPerThread],
|
|
::cuda::std::true_type /*time_slicing*/)
|
|
{
|
|
T temp_items[ItemsPerThread];
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int slice = 0; slice < TIME_SLICES; slice++)
|
|
{
|
|
__syncthreads();
|
|
|
|
const int slice_offset = TIME_SLICED_ITEMS * slice;
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = ranks[i] - slice_offset;
|
|
if (item_offset >= 0 && item_offset < WARP_TIME_SLICED_ITEMS)
|
|
{
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
if (warp_id == slice)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = lane_id * ItemsPerThread + i;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
temp_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
output_items[i] = temp_items[i];
|
|
}
|
|
}
|
|
|
|
//! @brief Exchanges data items annotated by rank into **striped** arrangement. Specialized for no timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStriped(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
OffsetT (&ranks)[ItemsPerThread],
|
|
::cuda::std::false_type /*time_slicing*/)
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = ranks[i];
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * BLOCK_THREADS + linear_tid;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @brief Exchanges data items annotated by rank into **striped** arrangement. Specialized for warp-timeslicing.
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items to exchange, converting between **blocked** and **striped** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStriped(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
OffsetT (&ranks)[ItemsPerThread],
|
|
::cuda::std::true_type /*time_slicing*/)
|
|
{
|
|
T temp_items[ItemsPerThread];
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int slice = 0; slice < TIME_SLICES; slice++)
|
|
{
|
|
const int slice_offset = slice * TIME_SLICED_ITEMS;
|
|
const int slice_oob = slice_offset + TIME_SLICED_ITEMS;
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = ranks[i] - slice_offset;
|
|
if (item_offset >= 0 && item_offset < WARP_TIME_SLICED_ITEMS)
|
|
{
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
detail::uninitialized_copy_single(temp_storage.buff + item_offset, input_items[i]);
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
// Read a strip of items
|
|
const int strip_offset = i * BLOCK_THREADS;
|
|
const int strip_oob = strip_offset + BLOCK_THREADS;
|
|
|
|
if (slice_offset < strip_oob && slice_oob > strip_offset)
|
|
{
|
|
int item_offset = strip_offset + linear_tid - slice_offset;
|
|
if (item_offset >= 0 && item_offset < TIME_SLICED_ITEMS)
|
|
{
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset += item_offset >> LOG_SMEM_BANKS;
|
|
}
|
|
temp_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
output_items[i] = temp_items[i];
|
|
}
|
|
}
|
|
|
|
public:
|
|
//! @name Collective constructors
|
|
//! @{
|
|
|
|
//! @brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
|
//!
|
|
//! @rst
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//! @endrst
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE BlockExchange()
|
|
: temp_storage(PrivateStorage())
|
|
{}
|
|
|
|
//! @brief Collective constructor using the specified memory allocation as temporary storage.
|
|
//!
|
|
//! @rst
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//! @endrst
|
|
//!
|
|
//! @param[in] temp_storage Reference to memory allocation having layout type TempStorage
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE BlockExchange(TempStorage& temp_storage)
|
|
: temp_storage(temp_storage.Alias())
|
|
{}
|
|
|
|
//! @}
|
|
//! @name Structured exchanges
|
|
//! @{
|
|
|
|
//! @rst
|
|
//! Transposes data items from **striped** arrangement to **blocked** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//! Snippet
|
|
//! +++++++
|
|
//!
|
|
//! The code snippet below illustrates the conversion from a "striped" to a "blocked" arrangement
|
|
//! of 512 integer items partitioned across 128 threads where each thread owns 4 items.
|
|
//!
|
|
//! .. code-block:: c++
|
|
//!
|
|
//! #include <cub/cub.cuh> // or equivalently <cub/block/block_exchange.cuh>
|
|
//!
|
|
//! __global__ void ExampleKernel(int *d_data, ...)
|
|
//! {
|
|
//! // Specialize BlockExchange for a 1D block of 128 threads owning 4 integer items each
|
|
//! using BlockExchange = cub::BlockExchange<int, 128, 4>;
|
|
//!
|
|
//! // Allocate shared memory for BlockExchange
|
|
//! __shared__ typename BlockExchange::TempStorage temp_storage;
|
|
//!
|
|
//! // Load a tile of ordered data into a striped arrangement across block threads
|
|
//! int thread_data[4];
|
|
//! cub::LoadDirectStriped<128>(threadIdx.x, d_data, thread_data);
|
|
//!
|
|
//! // Collectively exchange data into a blocked arrangement across threads
|
|
//! BlockExchange(temp_storage).StripedToBlocked(thread_data, thread_data);
|
|
//! }
|
|
//!
|
|
//! Suppose the set of striped input ``thread_data`` across the block of threads is ``{ [0,128,256,384],
|
|
//! [1,129,257,385], ..., [127,255,383,511] }`` after loading from device-accessible memory. The corresponding output
|
|
//! ``thread_data`` in those threads will be ``{ [0,1,2,3], [4,5,6,7], [8,9,10,11], ..., [508,509,510,511] }``.
|
|
//! @endrst
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void
|
|
StripedToBlocked(const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread])
|
|
{
|
|
StripedToBlocked(input_items, output_items, detail::bool_constant_v<WarpTimeSlicing>);
|
|
}
|
|
|
|
//! @rst
|
|
//! Transposes data items from **blocked** arrangement to **striped** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//! Snippet
|
|
//! +++++++
|
|
//!
|
|
//! The code snippet below illustrates the conversion from a "blocked" to a "striped" arrangement
|
|
//! of 512 integer items partitioned across 128 threads where each thread owns 4 items.
|
|
//!
|
|
//! .. code-block:: c++
|
|
//!
|
|
//! #include <cub/cub.cuh> // or equivalently <cub/block/block_exchange.cuh>
|
|
//!
|
|
//! __global__ void ExampleKernel(int *d_data, ...)
|
|
//! {
|
|
//! // Specialize BlockExchange for a 1D block of 128 threads owning 4 integer items each
|
|
//! using BlockExchange = cub::BlockExchange<int, 128, 4>;
|
|
//!
|
|
//! // Allocate shared memory for BlockExchange
|
|
//! __shared__ typename BlockExchange::TempStorage temp_storage;
|
|
//!
|
|
//! // Obtain a segment of consecutive items that are blocked across threads
|
|
//! int thread_data[4];
|
|
//! ...
|
|
//!
|
|
//! // Collectively exchange data into a striped arrangement across threads
|
|
//! BlockExchange(temp_storage).BlockedToStriped(thread_data, thread_data);
|
|
//!
|
|
//! // Store data striped across block threads into an ordered tile
|
|
//! cub::StoreDirectStriped<STORE_DEFAULT, 128>(threadIdx.x, d_data, thread_data);
|
|
//! }
|
|
//!
|
|
//! Suppose the set of blocked input ``thread_data`` across the block of threads is ``{ [0,1,2,3], [4,5,6,7],
|
|
//! [8,9,10,11], ..., [508,509,510,511] }``. The corresponding output ``thread_data`` in those threads will be
|
|
//! ``{ [0,128,256,384], [1,129,257,385], ..., [127,255,383,511] }`` in preparation for storing to device-accessible
|
|
//! memory.
|
|
//! @endrst
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void
|
|
BlockedToStriped(const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread])
|
|
{
|
|
BlockedToStriped(input_items, output_items, detail::bool_constant_v<WarpTimeSlicing>);
|
|
}
|
|
|
|
//! @rst
|
|
//! Transposes data items from **warp-striped** arrangement to **blocked** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//!
|
|
//! Snippet
|
|
//! +++++++
|
|
//!
|
|
//! The code snippet below illustrates the conversion from a "warp-striped" to a "blocked"
|
|
//! arrangement of 512 integer items partitioned across 128 threads where each thread owns 4
|
|
//! items.
|
|
//!
|
|
//! .. code-block:: c++
|
|
//!
|
|
//! #include <cub/cub.cuh> // or equivalently <cub/block/block_exchange.cuh>
|
|
//!
|
|
//! __global__ void ExampleKernel(int *d_data, ...)
|
|
//! {
|
|
//! // Specialize BlockExchange for a 1D block of 128 threads owning 4 integer items each
|
|
//! using BlockExchange = cub::BlockExchange<int, 128, 4>;
|
|
//!
|
|
//! // Allocate shared memory for BlockExchange
|
|
//! __shared__ typename BlockExchange::TempStorage temp_storage;
|
|
//!
|
|
//! // Load a tile of ordered data into a warp-striped arrangement across warp threads
|
|
//! int thread_data[4];
|
|
//! cub::LoadSWarptriped<LOAD_DEFAULT>(threadIdx.x, d_data, thread_data);
|
|
//!
|
|
//! // Collectively exchange data into a blocked arrangement across threads
|
|
//! BlockExchange(temp_storage).WarpStripedToBlocked(thread_data);
|
|
//! }
|
|
//!
|
|
//! Suppose the set of warp-striped input ``thread_data`` across the block of threads is ``{ [0,32,64,96],
|
|
//! [1,33,65,97], [2,34,66,98], ..., [415,447,479,511] }`` after loading from device-accessible memory. (The first 128
|
|
//! items are striped across the first warp of 32 threads, the second 128 items are striped across the second warp,
|
|
//! etc.) The corresponding output ``thread_data`` in those threads will be ``{ [0,1,2,3], [4,5,6,7], [8,9,10,11],
|
|
//! ..., [508,509,510,511] }``.
|
|
//! @endrst
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void
|
|
WarpStripedToBlocked(const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread])
|
|
{
|
|
WarpStripedToBlocked(input_items, output_items, detail::bool_constant_v<WarpTimeSlicing>);
|
|
}
|
|
|
|
//! @rst
|
|
//! Transposes data items from **blocked** arrangement to **warp-striped** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//!
|
|
//! Snippet
|
|
//! +++++++
|
|
//!
|
|
//! The code snippet below illustrates the conversion from a "blocked" to a "warp-striped"
|
|
//! arrangement of 512 integer items partitioned across 128 threads where each thread owns 4
|
|
//! items.
|
|
//!
|
|
//! .. code-block:: c++
|
|
//!
|
|
//! #include <cub/cub.cuh> // or equivalently <cub/block/block_exchange.cuh>
|
|
//!
|
|
//! __global__ void ExampleKernel(int *d_data, ...)
|
|
//! {
|
|
//! // Specialize BlockExchange for a 1D block of 128 threads owning 4 integer items each
|
|
//! using BlockExchange = cub::BlockExchange<int, 128, 4>;
|
|
//!
|
|
//! // Allocate shared memory for BlockExchange
|
|
//! __shared__ typename BlockExchange::TempStorage temp_storage;
|
|
//!
|
|
//! // Obtain a segment of consecutive items that are blocked across threads
|
|
//! int thread_data[4];
|
|
//! ...
|
|
//!
|
|
//! // Collectively exchange data into a warp-striped arrangement across threads
|
|
//! BlockExchange(temp_storage).BlockedToWarpStriped(thread_data, thread_data);
|
|
//!
|
|
//! // Store data striped across warp threads into an ordered tile
|
|
//! cub::StoreDirectStriped<STORE_DEFAULT, 128>(threadIdx.x, d_data, thread_data);
|
|
//! }
|
|
//!
|
|
//! Suppose the set of blocked input ``thread_data`` across the block of threads is ``{ [0,1,2,3], [4,5,6,7],
|
|
//! [8,9,10,11], ..., [508,509,510,511] }``. The corresponding output ``thread_data`` in those threads will be
|
|
//! ``{ [0,32,64,96], [1,33,65,97], [2,34,66,98], ..., [415,447,479,511] }`` in preparation for storing to
|
|
//! device-accessible memory. (The first 128 items are striped across the first warp of 32 threads, the second 128
|
|
//! items are striped across the second warp, etc.)
|
|
//! @endrst
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
template <typename OutputT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void
|
|
BlockedToWarpStriped(const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread])
|
|
{
|
|
BlockedToWarpStriped(input_items, output_items, detail::bool_constant_v<WarpTimeSlicing>);
|
|
}
|
|
|
|
//! @}
|
|
//! @name Scatter exchanges
|
|
//! @{
|
|
|
|
//! @rst
|
|
//! Exchanges data items annotated by rank into **blocked** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//! @endrst
|
|
//!
|
|
//! @tparam OffsetT
|
|
//! **[inferred]** Signed integer type for local offsets
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToBlocked(
|
|
const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread])
|
|
{
|
|
ScatterToBlocked(input_items, output_items, ranks, detail::bool_constant_v<WarpTimeSlicing>);
|
|
}
|
|
|
|
//! @rst
|
|
//! Exchanges data items annotated by rank into **striped** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//! @endrst
|
|
//!
|
|
//! @tparam OffsetT
|
|
//! **[inferred]** Signed integer type for local offsets
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStriped(
|
|
const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread])
|
|
{
|
|
ScatterToStriped(input_items, output_items, ranks, detail::bool_constant_v<WarpTimeSlicing>);
|
|
}
|
|
|
|
//! @rst
|
|
//! Exchanges data items annotated by rank into **striped** arrangement. Items with rank -1 are not exchanged.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//! @endrst
|
|
//!
|
|
//! @tparam OffsetT
|
|
//! **[inferred]** Signed integer type for local offsets
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
template <typename OutputT, typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStripedGuarded(
|
|
const T (&input_items)[ItemsPerThread], OutputT (&output_items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread])
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = ranks[i];
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
if (ranks[i] >= 0)
|
|
{
|
|
temp_storage.buff[item_offset] = input_items[i];
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * BLOCK_THREADS + linear_tid;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @rst
|
|
//! Exchanges valid data items annotated by rank into **striped** arrangement.
|
|
//!
|
|
//! .. versionadded:: 2.2.0
|
|
//! First appears in CUDA Toolkit 12.3.
|
|
//!
|
|
//! - @smemreuse
|
|
//!
|
|
//! @endrst
|
|
//!
|
|
//! @tparam OffsetT
|
|
//! **[inferred]** Signed integer type for local offsets
|
|
//!
|
|
//! @tparam ValidFlag
|
|
//! **[inferred]** FlagT type denoting which items are valid
|
|
//!
|
|
//! @param[in] input_items
|
|
//! Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[out] output_items
|
|
//! Items from exchange, converting between **striped** and **blocked** arrangements.
|
|
//!
|
|
//! @param[in] ranks
|
|
//! Corresponding scatter ranks
|
|
//!
|
|
//! @param[in] is_valid
|
|
//! Corresponding flag denoting item validity
|
|
template <typename OutputT, typename OffsetT, typename ValidFlag>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStripedFlagged(
|
|
const T (&input_items)[ItemsPerThread],
|
|
OutputT (&output_items)[ItemsPerThread],
|
|
OffsetT (&ranks)[ItemsPerThread],
|
|
ValidFlag (&is_valid)[ItemsPerThread])
|
|
{
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = ranks[i];
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
if (is_valid[i])
|
|
{
|
|
temp_storage.buff[item_offset] = input_items[i];
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
_CCCL_PRAGMA_UNROLL_FULL()
|
|
for (int i = 0; i < ItemsPerThread; i++)
|
|
{
|
|
int item_offset = i * BLOCK_THREADS + linear_tid;
|
|
if constexpr (INSERT_PADDING)
|
|
{
|
|
item_offset = (item_offset >> LOG_SMEM_BANKS) + item_offset;
|
|
}
|
|
output_items[i] = temp_storage.buff[item_offset];
|
|
}
|
|
}
|
|
|
|
//! @}
|
|
|
|
#ifndef _CCCL_DOXYGEN_INVOKED // Do not document
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void StripedToBlocked(T (&items)[ItemsPerThread])
|
|
{
|
|
StripedToBlocked(items, items);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void BlockedToStriped(T (&items)[ItemsPerThread])
|
|
{
|
|
BlockedToStriped(items, items);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void WarpStripedToBlocked(T (&items)[ItemsPerThread])
|
|
{
|
|
WarpStripedToBlocked(items, items);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void BlockedToWarpStriped(T (&items)[ItemsPerThread])
|
|
{
|
|
BlockedToWarpStriped(items, items);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
///
|
|
/// @param[in] ranks
|
|
/// Corresponding scatter ranks
|
|
template <typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToBlocked(T (&items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread])
|
|
{
|
|
ScatterToBlocked(items, items, ranks);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
/// @param[in] ranks
|
|
/// Corresponding scatter ranks
|
|
template <typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStriped(T (&items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread])
|
|
{
|
|
ScatterToStriped(items, items, ranks);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
/// @param[in] ranks
|
|
/// Corresponding scatter ranks
|
|
template <typename OffsetT>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void
|
|
ScatterToStripedGuarded(T (&items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread])
|
|
{
|
|
ScatterToStripedGuarded(items, items, ranks);
|
|
}
|
|
|
|
/// @param[in-out] items
|
|
/// Items to exchange, converting between **striped** and **blocked** arrangements.
|
|
/// @param[in] ranks
|
|
/// Corresponding scatter ranks
|
|
/// @param[in] is_valid
|
|
/// Corresponding flag denoting item validity
|
|
template <typename OffsetT, typename ValidFlag>
|
|
_CCCL_DEVICE _CCCL_FORCEINLINE void ScatterToStripedFlagged(
|
|
T (&items)[ItemsPerThread], OffsetT (&ranks)[ItemsPerThread], ValidFlag (&is_valid)[ItemsPerThread])
|
|
{
|
|
ScatterToStripedFlagged(items, items, ranks, is_valid);
|
|
}
|
|
|
|
#endif // _CCCL_DOXYGEN_INVOKED
|
|
};
|
|
|
|
CUB_NAMESPACE_END
|