[CCCL] 瘦身 + 补全: 移除 cudax/python/libcudacxx-tests 冗余文件, 新增 c2h 测试助手 + cmake 构建系统 + 8 个 CUDA thrust examples
变更摘要:
- 删除: cudax/ (783 files, 7.2M) — 实验性组件,竞赛不需要
- 删除: python/ (226 files, 2.0M) — Python 绑定,竞赛不需要
- 删除: libcudacxx/{test,benchmarks,codegen,cmake,share} (4432 files, 31M)
保留: libcudacxx/include/ (1463 headers, cuda::std 编译依赖)
- 新增: c2h/ (27 files) — CUB Catch2 测试辅助头文件,编译 243 个测试必需
- 新增: cmake/ (29 files) — CCCL 原生 CMake 构建系统
- 新增: thrust/examples/cuda/ (7 files) + cpp_integration/ (1 file)
async_reduce, custom_temporary_allocation, explicit_cuda_stream,
global_device_vector, range_view, unwrap_pointer, wrap_pointer, device
结果: cccl_upstream 从 74M→35M (瘦身 53%), 核心内容 100% 保留:
27/27 tuning headers, 78 benchmarks, 243 tests,
60 thrust examples, 18 CUB examples, 全部编译头文件
This commit is contained in:
@@ -1,236 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_CONTIGUOUS_H
|
||||
#define _CUDAX__COPY_CONTIGUOUS_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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/device/dispatch/tuning/tuning_transform.cuh>
|
||||
|
||||
#include <cuda/__cmath/ceil_div.h>
|
||||
#include <cuda/__device/all_devices.h>
|
||||
#include <cuda/__device/arch_id.h>
|
||||
#include <cuda/__device/arch_traits.h>
|
||||
#include <cuda/__launch/configuration.h>
|
||||
#include <cuda/__launch/launch.h>
|
||||
#include <cuda/__stream/stream_ref.h>
|
||||
#include <cuda/std/__algorithm/max.h>
|
||||
#include <cuda/std/__cstddef/types.h>
|
||||
#include <cuda/std/__mdspan/default_accessor.h>
|
||||
#include <cuda/std/__type_traits/integral_constant.h>
|
||||
#include <cuda/std/array>
|
||||
|
||||
#include <cuda/experimental/__copy/tensor_copy_utils.cuh>
|
||||
#include <cuda/experimental/__copy/tensor_iterator.cuh>
|
||||
#include <cuda/experimental/__copy_bytes/types.cuh>
|
||||
|
||||
#include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Tiled copy kernel for contiguous innermost dimension.
|
||||
//!
|
||||
//! Uses a 2D grid: blockIdx.x = tile along inner dimension, blockIdx.y = outer index.
|
||||
//! Threads within a block stride over the tile, reading from the source and writing to the
|
||||
//! destination via accessors. The coordinate iterator maps linear indices to multi-dimensional
|
||||
//! coordinates, which are then used with per-tensor strides for the actual memory access.
|
||||
//!
|
||||
//! @param[in] __config Kernel launch configuration
|
||||
//! @param[in] __src_ptr Pointer to source data
|
||||
//! @param[in] __src_strides Per-dimension strides for the source tensor
|
||||
//! @param[in] __src_accessor Accessor for reading source elements
|
||||
//! @param[out] __dst_ptr Pointer to destination data
|
||||
//! @param[in] __dst_strides Per-dimension strides for the destination tensor
|
||||
//! @param[in] __dst_accessor Accessor for writing destination elements
|
||||
//! @param[in] __coord_iter Coordinate iterator for multi-dimensional index mapping
|
||||
//! @param[in] __inner_size Extent of the contiguous innermost dimension
|
||||
template <typename _Config,
|
||||
int _TileSize,
|
||||
typename _TpSrc,
|
||||
typename _TpDst,
|
||||
typename _SrcAccessor,
|
||||
typename _DstAccessor,
|
||||
typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
::cuda::std::size_t _Rank>
|
||||
__global__ void __copy_contiguous_kernel(
|
||||
_CCCL_GRID_CONSTANT const _Config __config,
|
||||
_CCCL_GRID_CONSTANT const _TpSrc* const _CCCL_RESTRICT __src_ptr,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTIn, _Rank> __src_strides,
|
||||
_CCCL_GRID_CONSTANT const _SrcAccessor __src_accessor,
|
||||
_CCCL_GRID_CONSTANT _TpDst* const _CCCL_RESTRICT __dst_ptr,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTOut, _Rank> __dst_strides,
|
||||
_CCCL_GRID_CONSTANT const _DstAccessor __dst_accessor,
|
||||
_CCCL_GRID_CONSTANT const __tensor_coord_iterator<_ExtentT, _Rank> __coord_iter,
|
||||
_CCCL_GRID_CONSTANT const _ExtentT __inner_size)
|
||||
{
|
||||
using __partial_tensor_src = __partial_tensor<const _TpSrc, _StrideTIn, _Rank, _SrcAccessor>;
|
||||
using __partial_tensor_dst = __partial_tensor<_TpDst, _StrideTOut, _Rank, _DstAccessor>;
|
||||
const auto __thread_id = ::cuda::gpu_thread.rank_as<_ExtentT>(::cuda::block, __config);
|
||||
const auto __block_idx = ::cuda::block.index_as<_ExtentT>(::cuda::grid);
|
||||
constexpr auto __block_size = ::cuda::gpu_thread.count_as<int>(::cuda::block, __config);
|
||||
const __partial_tensor_src __src{__src_ptr, __src_strides, __src_accessor};
|
||||
const __partial_tensor_dst __dst{__dst_ptr, __dst_strides, __dst_accessor};
|
||||
|
||||
const auto __tile_offset = __block_idx.x * _TileSize;
|
||||
const auto __outer_idx = __block_idx.y;
|
||||
const auto __remaining = __inner_size - __tile_offset;
|
||||
const auto __base_idx = __outer_idx * __inner_size + __tile_offset + __thread_id;
|
||||
|
||||
if (__remaining >= _TileSize)
|
||||
{
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __i = 0; __i < _TileSize; __i += __block_size)
|
||||
{
|
||||
const auto __coord = __coord_iter(__base_idx + __i);
|
||||
__dst(__coord) = __src(__coord);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __i = 0; __i < _TileSize; __i += __block_size)
|
||||
{
|
||||
if (__thread_id + __i < __remaining)
|
||||
{
|
||||
const auto __coord = __coord_iter(__base_idx + __i);
|
||||
__dst(__coord) = __src(__coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! @brief Query the minimum bytes-in-flight target for the current GPU architecture.
|
||||
//!
|
||||
//! Delegates to CUB's architecture-specific tuning.
|
||||
//! @return Bytes-in-flight target (e.g. 12KB for V100, 16KB for A100, 48KB for H200, 64KB for B200)
|
||||
[[nodiscard]] _CCCL_HOST_API inline int __bytes_in_flight() noexcept
|
||||
{
|
||||
const auto __dev_id = ::cuda::__driver::__cudevice_to_ordinal(::cuda::__driver::__ctxGetDevice());
|
||||
const auto __dev = ::cuda::devices[__dev_id];
|
||||
const auto __cc = ::cuda::device_attributes::compute_capability(__dev);
|
||||
return CUB_NS_QUALIFIER::detail::transform::cc_to_min_bytes_in_flight(__cc);
|
||||
}
|
||||
|
||||
// Compute the number of elements each thread copies for a given vector width.
|
||||
[[nodiscard]] _CCCL_HOST_API inline int __elem_per_thread(int __access_bytes, int __bytes_in_flight) noexcept
|
||||
{
|
||||
constexpr auto __threads_per_sm = 2048;
|
||||
return ::cuda::std::max(__bytes_in_flight / (__access_bytes * __threads_per_sm), 1);
|
||||
}
|
||||
|
||||
// Dispatch a callable with a compile-time tile size derived from a runtime value.
|
||||
template <typename _Op>
|
||||
_CCCL_HOST_API void __dispatch_tile_size(int __tile_size, _Op __op) noexcept
|
||||
{
|
||||
if (__tile_size >= 2048)
|
||||
{
|
||||
__op(::cuda::std::integral_constant<int, 2048>{});
|
||||
}
|
||||
else if (__tile_size >= 1024)
|
||||
{
|
||||
__op(::cuda::std::integral_constant<int, 1024>{});
|
||||
}
|
||||
else if (__tile_size >= 512)
|
||||
{
|
||||
__op(::cuda::std::integral_constant<int, 512>{});
|
||||
}
|
||||
else
|
||||
{
|
||||
__op(::cuda::std::integral_constant<int, 256>{});
|
||||
}
|
||||
}
|
||||
|
||||
//! @brief Launch the tiled copy kernel for contiguous innermost dimension.
|
||||
//!
|
||||
//! Computes tile size from the architecture-specific bytes-in-flight target, then dispatches
|
||||
//! the @ref __copy_contiguous_kernel with a compile-time tile size.
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor descriptor
|
||||
//! @param[in] __dst Destination raw tensor descriptor
|
||||
//! @param[in] __stream CUDA stream for asynchronous execution
|
||||
//! @param[in] __src_accessor Accessor for reading source elements
|
||||
//! @param[in] __dst_accessor Accessor for writing destination elements
|
||||
template <typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
typename _TpIn,
|
||||
typename _TpOut,
|
||||
::cuda::std::size_t _Rank,
|
||||
typename _SrcAccessor = ::cuda::std::default_accessor<_TpIn>,
|
||||
typename _DstAccessor = ::cuda::std::default_accessor<_TpOut>>
|
||||
_CCCL_HOST_API void __launch_copy_contiguous_kernel(
|
||||
const __raw_tensor<_ExtentT, _StrideTIn, _TpIn, _Rank>& __src,
|
||||
const __raw_tensor<_ExtentT, _StrideTOut, _TpOut, _Rank>& __dst,
|
||||
::cuda::stream_ref __stream,
|
||||
const _SrcAccessor& __src_accessor = {},
|
||||
const _DstAccessor& __dst_accessor = {})
|
||||
{
|
||||
constexpr int __block_size = 256;
|
||||
const auto __bytes_in_flight = ::cuda::experimental::__bytes_in_flight();
|
||||
const auto __elems_per_thread =
|
||||
::cuda::experimental::__elem_per_thread(static_cast<int>(sizeof(_TpIn)), __bytes_in_flight);
|
||||
const auto __tile_size_rt = __block_size * __elems_per_thread;
|
||||
|
||||
::cuda::experimental::__dispatch_tile_size(__tile_size_rt, [&](auto __tile_constant) {
|
||||
constexpr int __tile_size = decltype(__tile_constant)::value;
|
||||
const auto __inner_size = __src.__extents[0];
|
||||
const auto __outer_size = ::cuda::experimental::__total_size(__src) / __inner_size;
|
||||
const auto __num_inner_tiles = ::cuda::ceil_div(__inner_size, __tile_size);
|
||||
constexpr auto __arch_limits = ::cuda::__common_arch_traits(::cuda::arch_id::sm_90);
|
||||
_CCCL_ASSERT(__num_inner_tiles <= _ExtentT(__arch_limits.max_grid_dim_x),
|
||||
"grid x-dimension exceeds the maximum grid size");
|
||||
_CCCL_ASSERT(__outer_size <= _ExtentT(__arch_limits.max_grid_dim_y),
|
||||
"grid y-dimension exceeds the maximum grid size");
|
||||
const auto __grid_dims = ::dim3(static_cast<unsigned>(__num_inner_tiles), static_cast<unsigned>(__outer_size));
|
||||
const auto __config = ::cuda::make_config(::cuda::block_dims<__block_size>(), ::cuda::grid_dims(__grid_dims));
|
||||
|
||||
const __tensor_coord_iterator<_ExtentT, _Rank> __coord_iter{__src.__extents};
|
||||
const auto __kernel = ::cuda::experimental::__copy_contiguous_kernel<
|
||||
decltype(__config),
|
||||
__tile_size,
|
||||
_TpIn,
|
||||
_TpOut,
|
||||
_SrcAccessor,
|
||||
_DstAccessor,
|
||||
_ExtentT,
|
||||
_StrideTIn,
|
||||
_StrideTOut,
|
||||
_Rank>;
|
||||
|
||||
::cuda::launch(
|
||||
__stream,
|
||||
__config,
|
||||
__kernel,
|
||||
__src.__data,
|
||||
__src.__strides,
|
||||
__src_accessor,
|
||||
__dst.__data,
|
||||
__dst.__strides,
|
||||
__dst_accessor,
|
||||
__coord_iter,
|
||||
__inner_size);
|
||||
});
|
||||
}
|
||||
} // namespace cuda::experimental
|
||||
|
||||
#include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // _CUDAX__COPY_CONTIGUOUS_H
|
||||
@@ -1,149 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_OPTIMIZED_H
|
||||
#define _CUDAX__COPY_OPTIMIZED_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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 <cuda/__cmath/ceil_div.h>
|
||||
#include <cuda/__stream/stream_ref.h>
|
||||
#include <cuda/launch>
|
||||
#include <cuda/std/__cstddef/types.h>
|
||||
#include <cuda/std/__mdspan/default_accessor.h>
|
||||
#include <cuda/std/array>
|
||||
|
||||
#include <cuda/experimental/__copy/tensor_iterator.cuh>
|
||||
#include <cuda/experimental/__copy_bytes/types.cuh>
|
||||
|
||||
#include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Element-wise copy kernel for strided tensor data.
|
||||
//!
|
||||
//! Each thread copies one element at a time using a grid-stride loop, mapping linear indices to
|
||||
//! multi-dimensional coordinates via @ref __tensor_coord_iterator.
|
||||
//!
|
||||
//! @param[in] __config Kernel launch configuration
|
||||
//! @param[in] __src_ptr Pointer to source data
|
||||
//! @param[in] __src_strides Per-dimension strides for the source tensor
|
||||
//! @param[in] __src_accessor Accessor for reading source elements
|
||||
//! @param[out] __dst_ptr Pointer to destination data
|
||||
//! @param[in] __dst_strides Per-dimension strides for the destination tensor
|
||||
//! @param[in] __dst_accessor Accessor for writing destination elements
|
||||
//! @param[in] __coord_iter Coordinate iterator for multi-dimensional index mapping
|
||||
//! @param[in] __tensor_size Total number of elements to copy
|
||||
template <typename _Config,
|
||||
typename _TpSrc,
|
||||
typename _TpDst,
|
||||
typename _SrcAccessor,
|
||||
typename _DstAccessor,
|
||||
typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
::cuda::std::size_t _Rank>
|
||||
__global__ void __copy_optimized_kernel(
|
||||
_CCCL_GRID_CONSTANT const _Config __config,
|
||||
_CCCL_GRID_CONSTANT const _TpSrc* const _CCCL_RESTRICT __src_ptr,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTIn, _Rank> __src_strides,
|
||||
_CCCL_GRID_CONSTANT const _SrcAccessor __src_accessor,
|
||||
_CCCL_GRID_CONSTANT _TpDst* const _CCCL_RESTRICT __dst_ptr,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTOut, _Rank> __dst_strides,
|
||||
_CCCL_GRID_CONSTANT const _DstAccessor __dst_accessor,
|
||||
_CCCL_GRID_CONSTANT const __tensor_coord_iterator<_ExtentT, _Rank> __coord_iter,
|
||||
_CCCL_GRID_CONSTANT const _ExtentT __tensor_size)
|
||||
{
|
||||
using __partial_tensor_src = __partial_tensor<const _TpSrc, _StrideTIn, _Rank, _SrcAccessor>;
|
||||
using __partial_tensor_dst = __partial_tensor<_TpDst, _StrideTOut, _Rank, _DstAccessor>;
|
||||
const auto __idx = ::cuda::gpu_thread.rank_as<_ExtentT>(::cuda::grid, __config);
|
||||
const auto __stride = ::cuda::gpu_thread.count_as<_ExtentT>(::cuda::grid, __config);
|
||||
const __partial_tensor_src __src{__src_ptr, __src_strides, __src_accessor};
|
||||
const __partial_tensor_dst __dst{__dst_ptr, __dst_strides, __dst_accessor};
|
||||
|
||||
for (auto __i = __idx; __i < __tensor_size; __i += __stride)
|
||||
{
|
||||
const auto __coord = __coord_iter(__i);
|
||||
__dst(__coord) = __src(__coord);
|
||||
if constexpr (sizeof(_ExtentT) <= 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! @brief Launch a naive element-wise copy kernel for strided tensor data.
|
||||
//!
|
||||
//! Each thread copies one element at a time using a grid-stride loop. Coordinates are
|
||||
//! computed from linear indices via @ref __tensor_coord_iterator.
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor descriptor
|
||||
//! @param[out] __dst Destination raw tensor descriptor
|
||||
//! @param[in] __tensor_size Total number of elements to copy
|
||||
//! @param[in] __stream CUDA stream for asynchronous execution
|
||||
//! @param[in] __src_accessor Accessor for reading source elements
|
||||
//! @param[in] __dst_accessor Accessor for writing destination elements
|
||||
template <typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
typename _TpIn,
|
||||
typename _TpOut,
|
||||
::cuda::std::size_t _Rank,
|
||||
typename _SrcAccessor = ::cuda::std::default_accessor<_TpIn>,
|
||||
typename _DstAccessor = ::cuda::std::default_accessor<_TpOut>>
|
||||
_CCCL_HOST_API void __copy_optimized(
|
||||
const __raw_tensor<_ExtentT, _StrideTIn, _TpIn, _Rank>& __src,
|
||||
const __raw_tensor<_ExtentT, _StrideTOut, _TpOut, _Rank>& __dst,
|
||||
_ExtentT __tensor_size,
|
||||
::cuda::stream_ref __stream,
|
||||
const _SrcAccessor& __src_accessor = {},
|
||||
const _DstAccessor& __dst_accessor = {}) noexcept
|
||||
{
|
||||
constexpr int __block_size = 256;
|
||||
const __tensor_coord_iterator<_ExtentT, _Rank> __coord_iter(__src.__extents);
|
||||
const auto __grid_size = ::cuda::ceil_div(__tensor_size, _ExtentT{__block_size});
|
||||
const auto __config = ::cuda::make_config(::cuda::block_dims<__block_size>(), ::cuda::grid_dims(__grid_size));
|
||||
const auto& __kernel = ::cuda::experimental::__copy_optimized_kernel<
|
||||
decltype(__config),
|
||||
_TpIn,
|
||||
_TpOut,
|
||||
_SrcAccessor,
|
||||
_DstAccessor,
|
||||
_ExtentT,
|
||||
_StrideTIn,
|
||||
_StrideTOut,
|
||||
_Rank>;
|
||||
|
||||
::cuda::launch(
|
||||
__stream,
|
||||
__config,
|
||||
__kernel,
|
||||
__src.__data,
|
||||
__src.__strides,
|
||||
__src_accessor,
|
||||
__dst.__data,
|
||||
__dst.__strides,
|
||||
__dst_accessor,
|
||||
__coord_iter,
|
||||
__tensor_size);
|
||||
}
|
||||
} // namespace cuda::experimental
|
||||
|
||||
#include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // _CUDAX__COPY_OPTIMIZED_H
|
||||
@@ -1,442 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_COPY_SHARED_MEMORY_H
|
||||
#define _CUDAX__COPY_COPY_SHARED_MEMORY_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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 <cuda/__cmath/ceil_div.h>
|
||||
#include <cuda/__launch/configuration.h>
|
||||
#include <cuda/__launch/launch.h>
|
||||
#include <cuda/__stream/stream_ref.h>
|
||||
#include <cuda/std/__cstddef/types.h>
|
||||
#include <cuda/std/__mdspan/default_accessor.h>
|
||||
#include <cuda/std/__type_traits/make_unsigned.h>
|
||||
#include <cuda/std/__type_traits/remove_cv.h>
|
||||
#include <cuda/std/array>
|
||||
|
||||
#include <cuda/experimental/__copy/copy_shared_memory_utils.cuh>
|
||||
#include <cuda/experimental/__copy/tensor_iterator.cuh>
|
||||
#include <cuda/experimental/__copy_bytes/types.cuh>
|
||||
|
||||
#include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
//! Shared-memory tiled transpose for arbitrary-rank tensor copies.
|
||||
//!
|
||||
//! The overall idea is to decompose the tensors into tiles that can fit in shared memory.
|
||||
//! Each tile is assigned to a thread block. A tile can entirely represent a dimension or split the respective extent.
|
||||
//! The algorithm creates tiles over dimensions that provide coalesced accesses in the source and destination tensors.
|
||||
//!
|
||||
//! (1) Grid decomposition
|
||||
//! The tensor is partitioned into tiles whose per-dimension sizes are capped by warp size and shared-memory capacity.
|
||||
//! The total number of tiles (product of ceil(extent[d] / tile_size[d]) over all dimensions) becomes the 1-D grid size.
|
||||
//!
|
||||
//! (2) Block processing
|
||||
//! Each block handles one tile in two phases:
|
||||
//! 1. *Load*: threads cooperatively read source elements into shared memory.
|
||||
//! This requires additional logic to "transpose" the source tensor into a row-major order.
|
||||
//! The mapping is determined by using the source-tile permutation obtained by sorting by |src stride|.
|
||||
//! 2. *Store*: after a barrier, threads read shared memory in destination-coalesced order by using the
|
||||
//! destination-tile permutation obtained by sorting by |dst stride|.
|
||||
//!
|
||||
//! Boundary tiles that extend past the tensor extents fall back to a direct element-wise copy without shared memory.
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Compute the shared-memory offset for the XOR swizzle.
|
||||
//!
|
||||
//! @param[in] __offset The offset in the shared-memory tile.
|
||||
//! @return The offset in the shared-memory tile with the XOR swizzle applied.
|
||||
template <bool _UseXorSwizzle>
|
||||
[[nodiscard]] _CCCL_DEVICE_API __tile_extent_t __smem_offset(__tile_extent_t __offset) noexcept
|
||||
{
|
||||
if constexpr (_UseXorSwizzle)
|
||||
{
|
||||
static_assert(__max_tile_size == 32, "XOR shared-memory swizzle assumes 32 banks and 32-element tile modes");
|
||||
constexpr __tile_extent_t __swizzle_tile_size = __max_tile_size * __max_tile_size;
|
||||
const auto __outer = __offset / __swizzle_tile_size;
|
||||
const auto __offset_tile_rounded = __outer * __swizzle_tile_size;
|
||||
const auto __inner = __offset - __offset_tile_rounded;
|
||||
const auto __row = __inner / __max_tile_size;
|
||||
const auto __row_tile_rounded = __row * __max_tile_size;
|
||||
const auto __col = __inner - __row_tile_rounded;
|
||||
return __offset_tile_rounded + __row_tile_rounded + (__col ^ __row);
|
||||
}
|
||||
return __offset;
|
||||
}
|
||||
|
||||
//! @brief Shared-memory tiled transpose kernel for arbitrary-rank tensors.
|
||||
//!
|
||||
//! Each block processes one tile. Threads cooperatively iterate over tile elements with a stride loop. Full (interior)
|
||||
//! tiles use a two-phase shared-memory transpose: load source data into shared memory using source-coalesced ordering,
|
||||
//! then store from shared memory to destination using destination-coalesced ordering. Partial (boundary) tiles copy
|
||||
//! elements directly without shared memory.
|
||||
//!
|
||||
//! @param[in] __config Kernel launch configuration
|
||||
//! @param[in] __src_ptr Pointer to source data
|
||||
//! @param[in] __src_accessor Accessor for reading source elements
|
||||
//! @param[out] __dst_ptr Pointer to destination data
|
||||
//! @param[in] __dst_accessor Accessor for writing destination elements
|
||||
//! @param[in] __grid_iter Coordinate iterator for grid tile decomposition
|
||||
//! @param[in] __grid_tile_src_strides Per-dimension source strides scaled by tile sizes
|
||||
//! @param[in] __grid_tile_dst_strides Per-dimension destination strides scaled by tile sizes
|
||||
//! @param[in] __tile_perm_iter Coordinate iterator for src-permuted tile decomposition
|
||||
//! @param[in] __src_perm_src_strides Src-permuted source strides for loading
|
||||
//! @param[in] __tile_src_perm_smem_strides Src-permuted shared memory strides for loading
|
||||
//! @param[in] __tile_dst_perm_iter Coordinate iterator for dst-permuted tile decomposition
|
||||
//! @param[in] __dst_perm_dst_strides Dst-permuted destination strides for storing
|
||||
//! @param[in] __tile_dst_smem_strides Dst-permuted shared memory strides for storing
|
||||
//! @param[in] __dst_strides Per-dimension destination strides for partial tiles
|
||||
//! @param[in] __tile_total_size Total number of elements in one tile
|
||||
//! @param[in] __tile_sizes Per-dimension tile extents
|
||||
//! @param[in] __extents Per-dimension tensor extents (for partial-tile bounds)
|
||||
//! @param[in] __src_strides Per-dimension source strides (for partial-tile access)
|
||||
template <bool _UseXorSwizzle,
|
||||
typename _Config,
|
||||
::cuda::std::size_t _MaxRankUZ,
|
||||
typename _TpSrc,
|
||||
typename _TpDst,
|
||||
typename _SrcAccessor,
|
||||
typename _DstAccessor,
|
||||
typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut>
|
||||
__global__ void __copy_shared_mem_kernel(
|
||||
_CCCL_GRID_CONSTANT const _Config __config,
|
||||
const _TpSrc* _CCCL_RESTRICT __src_ptr,
|
||||
_CCCL_GRID_CONSTANT const _SrcAccessor __src_accessor,
|
||||
_TpDst* _CCCL_RESTRICT __dst_ptr,
|
||||
_CCCL_GRID_CONSTANT const _DstAccessor __dst_accessor,
|
||||
_CCCL_GRID_CONSTANT const __tensor_coord_iterator<_ExtentT, _MaxRankUZ> __grid_iter,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTIn, _MaxRankUZ> __grid_tile_src_strides,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTOut, _MaxRankUZ> __grid_tile_dst_strides,
|
||||
_CCCL_GRID_CONSTANT const __tensor_coord_iterator<__tile_extent_t, _MaxRankUZ> __tile_perm_iter,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTIn, _MaxRankUZ> __src_perm_src_strides,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<__tile_extent_t, _MaxRankUZ> __tile_src_perm_smem_strides,
|
||||
_CCCL_GRID_CONSTANT const __tensor_coord_iterator<__tile_extent_t, _MaxRankUZ> __tile_dst_perm_iter,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTOut, _MaxRankUZ> __dst_perm_dst_strides,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<__tile_extent_t, _MaxRankUZ> __tile_dst_perm_smem_strides,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTOut, _MaxRankUZ> __dst_strides,
|
||||
_CCCL_GRID_CONSTANT const int __tile_total_size,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<__tile_extent_t, _MaxRankUZ> __tile_sizes,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_ExtentT, _MaxRankUZ> __extents,
|
||||
_CCCL_GRID_CONSTANT const ::cuda::std::array<_StrideTIn, _MaxRankUZ> __src_strides)
|
||||
{
|
||||
constexpr auto __max_rank = int{_MaxRankUZ};
|
||||
// Grid tile decomposition: map linearized block index to src/dst base offsets
|
||||
// __grid_coords: linear tile index -> multi-dimensional coordinates (array)
|
||||
const auto __grid_index = ::cuda::block.index_as<_ExtentT>(::cuda::grid).x;
|
||||
const auto __grid_coords = __grid_iter(__grid_index);
|
||||
|
||||
{
|
||||
_StrideTIn __src_base = 0;
|
||||
_StrideTOut __dst_base = 0;
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __k = 0; __k < __max_rank; ++__k)
|
||||
{
|
||||
__src_base += static_cast<_StrideTIn>(__grid_coords[__k]) * __grid_tile_src_strides[__k];
|
||||
__dst_base += static_cast<_StrideTOut>(__grid_coords[__k]) * __grid_tile_dst_strides[__k];
|
||||
}
|
||||
__src_ptr += __src_base;
|
||||
__dst_ptr += __dst_base;
|
||||
}
|
||||
|
||||
// Partial tile detection: is the current tile full or partial?
|
||||
bool __is_full_tile = true;
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __k = 0; __k < __max_rank; ++__k)
|
||||
{
|
||||
const auto __block_start = __grid_coords[__k] * __tile_sizes[__k];
|
||||
if (__block_start + __tile_sizes[__k] > __extents[__k])
|
||||
{
|
||||
__is_full_tile = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch to Full-tile or Boundary case
|
||||
const auto __tid = ::cuda::gpu_thread.rank_as<int>(::cuda::block, __config);
|
||||
const auto __block_stride = ::cuda::gpu_thread.count_as<int>(::cuda::block, __config);
|
||||
using __partial_tensor_src = __partial_tensor<const _TpSrc, _StrideTIn, _MaxRankUZ, _SrcAccessor>;
|
||||
using __partial_tensor_dst = __partial_tensor<_TpDst, _StrideTOut, _MaxRankUZ, _DstAccessor>;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Full-tile shared-memory transpose
|
||||
if (__is_full_tile)
|
||||
{
|
||||
using _Tp = ::cuda::std::remove_cv_t<_TpSrc>;
|
||||
using __partial_tensor_smem =
|
||||
__partial_tensor<_Tp, __tile_extent_t, _MaxRankUZ, ::cuda::std::default_accessor<_Tp>>;
|
||||
|
||||
extern __shared__ char __smem_bytes[];
|
||||
auto* __smem = reinterpret_cast<_Tp*>(__smem_bytes);
|
||||
|
||||
// (1) load src to shared memory by using the src/tile-permuted ordering
|
||||
const __partial_tensor_src __src_tensor{__src_ptr, __src_perm_src_strides, __src_accessor};
|
||||
const __partial_tensor_smem __smem_tensor{
|
||||
__smem, __tile_src_perm_smem_strides, ::cuda::std::default_accessor<_Tp>{}};
|
||||
|
||||
for (auto __i = __tid; __i < __tile_total_size; __i += __block_stride)
|
||||
{
|
||||
const auto __coords = __tile_perm_iter(__i);
|
||||
const auto __raw_offset = __smem_tensor.__offset(__coords);
|
||||
const auto __swizzled_offset = ::cuda::experimental::__smem_offset<_UseXorSwizzle>(__raw_offset);
|
||||
__smem[__swizzled_offset] = __src_tensor(__coords);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// (2) store from shared memory to destination by using the dst/tile-permuted ordering
|
||||
const __partial_tensor_dst __dst_tensor{__dst_ptr, __dst_perm_dst_strides, __dst_accessor};
|
||||
const __partial_tensor_smem __smem_dst_tensor{
|
||||
__smem, __tile_dst_perm_smem_strides, ::cuda::std::default_accessor<_Tp>{}};
|
||||
|
||||
for (auto __i = __tid; __i < __tile_total_size; __i += __block_stride)
|
||||
{
|
||||
const auto __coords = __tile_dst_perm_iter(__i);
|
||||
const auto __raw_offset = __smem_dst_tensor.__offset(__coords);
|
||||
const auto __swizzled_offset = ::cuda::experimental::__smem_offset<_UseXorSwizzle>(__raw_offset);
|
||||
__dst_tensor(__coords) = __smem[__swizzled_offset];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Boundary direct-copy (no shared memory)
|
||||
else
|
||||
{
|
||||
using __uextent_t = ::cuda::std::make_unsigned_t<_ExtentT>;
|
||||
const __partial_tensor_src __src_tensor{__src_ptr, __src_strides, __src_accessor};
|
||||
const __partial_tensor_dst __dst_tensor{__dst_ptr, __dst_strides, __dst_accessor};
|
||||
|
||||
// Find the partial tile sizes and total number of elements
|
||||
::cuda::std::array<__tile_extent_t, __max_rank> __partial_tile_sizes{};
|
||||
int __partial_tile_total = 1;
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __k = 0; __k < __max_rank; ++__k)
|
||||
{
|
||||
const auto __block_start = static_cast<__uextent_t>(__grid_coords[__k] * __tile_sizes[__k]);
|
||||
const auto __diff = static_cast<__tile_extent_t>(__extents[__k] - __block_start);
|
||||
__partial_tile_sizes[__k] = ::cuda::std::min(__tile_sizes[__k], __diff);
|
||||
__partial_tile_total *= __partial_tile_sizes[__k];
|
||||
}
|
||||
|
||||
// map the linear index to the multi-dimensional coordinates and copy the elements
|
||||
for (auto __i = __tid; __i < __partial_tile_total; __i += __block_stride)
|
||||
{
|
||||
__tile_extent_t __linear = __i;
|
||||
::cuda::std::array<__tile_extent_t, __max_rank> __coords;
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __k = 0; __k < __max_rank; ++__k)
|
||||
{
|
||||
__coords[__k] = __linear % __partial_tile_sizes[__k];
|
||||
__linear /= __partial_tile_sizes[__k];
|
||||
}
|
||||
__dst_tensor(__coords) = __src_tensor(__coords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
//! @brief Launch the shared-memory tiled transpose kernel.
|
||||
//!
|
||||
//! Precomputes the source/destination-coalesced permutations and tile shapes, constructs coordinate iterators, then
|
||||
//! launches one block per tile.
|
||||
//!
|
||||
//! @pre `__src.__rank >= 2`
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor descriptor
|
||||
//! @param[out] __dst Destination raw tensor descriptor
|
||||
//! @param[in] __stream CUDA stream for asynchronous execution
|
||||
//! @param[in] __src_accessor Accessor for reading source elements
|
||||
//! @param[in] __dst_accessor Accessor for writing destination elements
|
||||
template <typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
typename _TpIn,
|
||||
typename _TpOut,
|
||||
::cuda::std::size_t _MaxRank,
|
||||
typename _SrcAccessor,
|
||||
typename _DstAccessor>
|
||||
_CCCL_HOST_API void __launch_copy_shared_mem_kernel(
|
||||
const __raw_tensor<_ExtentT, _StrideTIn, _TpIn, _MaxRank>& __src,
|
||||
const __raw_tensor<_ExtentT, _StrideTOut, _TpOut, _MaxRank>& __dst,
|
||||
::cuda::stream_ref __stream,
|
||||
const _SrcAccessor& __src_accessor = {},
|
||||
const _DstAccessor& __dst_accessor = {})
|
||||
{
|
||||
namespace cudax = ::cuda::experimental;
|
||||
using ::cuda::std::size_t;
|
||||
_CCCL_ASSERT(__src.__rank >= 2, "Rank must be at least 2 for shared memory transpose");
|
||||
|
||||
const auto __tiling = cudax::__find_shared_mem_tiling<_TpIn>(__src, __dst);
|
||||
const auto __tile_sizes = __tiling.__tile_sizes;
|
||||
const auto __rank = __src.__rank;
|
||||
const auto __tile_total_size = __tiling.__tile_total_size;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Find the grid size (number of blocks) and strides for block index decomposition
|
||||
::cuda::std::array<_ExtentT, _MaxRank> __grid_tile_sizes{};
|
||||
::cuda::std::array<_StrideTIn, _MaxRank> __grid_tile_src_strides{};
|
||||
::cuda::std::array<_StrideTOut, _MaxRank> __grid_tile_dst_strides{};
|
||||
_ExtentT __grid_size = 1;
|
||||
for (size_t __i = 0; __i < __rank; ++__i)
|
||||
{
|
||||
__grid_tile_sizes[__i] = ::cuda::ceil_div(__src.__extents[__i], static_cast<_ExtentT>(__tile_sizes[__i]));
|
||||
__grid_tile_src_strides[__i] = static_cast<_StrideTIn>(__tile_sizes[__i]) * __src.__strides[__i];
|
||||
__grid_tile_dst_strides[__i] = static_cast<_StrideTOut>(__tile_sizes[__i]) * __dst.__strides[__i];
|
||||
__grid_size *= __grid_tile_sizes[__i];
|
||||
}
|
||||
for (size_t __i = __rank; __i < _MaxRank; ++__i)
|
||||
{
|
||||
__grid_tile_sizes[__i] = 1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Reordered arrays for loading src and storing dst based on coalesced permutations
|
||||
::cuda::std::array<_StrideTIn, _MaxRank> __src_perm_src_strides{};
|
||||
::cuda::std::array<_StrideTOut, _MaxRank> __dst_perm_dst_strides{};
|
||||
::cuda::std::array<__tile_extent_t, _MaxRank> __tile_src_perm_sizes{};
|
||||
::cuda::std::array<__tile_extent_t, _MaxRank> __tile_dst_perm_sizes{};
|
||||
::cuda::std::array<__tile_extent_t, _MaxRank> __tile_src_perm_smem_strides{};
|
||||
::cuda::std::array<__tile_extent_t, _MaxRank> __tile_dst_perm_smem_strides{};
|
||||
::cuda::std::array<__tile_extent_t, _MaxRank> __canonical_strides{};
|
||||
__canonical_strides[0] = 1;
|
||||
for (size_t __i = 1; __i < __rank; ++__i)
|
||||
{
|
||||
__canonical_strides[__i] = __canonical_strides[__i - 1] * __tile_sizes[__i - 1];
|
||||
}
|
||||
for (size_t __i = 0; __i < __rank; ++__i)
|
||||
{
|
||||
const auto __p = __tiling.__src_perm[__i];
|
||||
__tile_src_perm_sizes[__i] = __tile_sizes[__p];
|
||||
__src_perm_src_strides[__i] = __src.__strides[__p];
|
||||
__tile_src_perm_smem_strides[__i] = __canonical_strides[__p];
|
||||
|
||||
const auto __q = __tiling.__dst_perm[__i];
|
||||
__tile_dst_perm_sizes[__i] = __tile_sizes[__q];
|
||||
__dst_perm_dst_strides[__i] = __dst.__strides[__q];
|
||||
__tile_dst_perm_smem_strides[__i] = __canonical_strides[__q];
|
||||
}
|
||||
for (size_t __i = __rank; __i < _MaxRank; ++__i)
|
||||
{
|
||||
__tile_src_perm_sizes[__i] = 1;
|
||||
__tile_dst_perm_sizes[__i] = 1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Construct coordinate iterators on the host (precomputed fast modulo/division)
|
||||
// namely, given a linear index, compute the multi-dimensional coordinates
|
||||
const __tensor_coord_iterator<_ExtentT, _MaxRank> __grid_iter{__grid_tile_sizes}; // grid tile index
|
||||
const __tensor_coord_iterator<__tile_extent_t, _MaxRank> __tile_perm_iter{__tile_src_perm_sizes}; // src -> shared
|
||||
// memory
|
||||
const __tensor_coord_iterator<__tile_extent_t, _MaxRank> __tile_dst_perm_iter{__tile_dst_perm_sizes}; // shared memory
|
||||
// -> dst
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Launch the kernel
|
||||
using __value_type = ::cuda::std::remove_cv_t<_TpIn>;
|
||||
const int __thread_block_size = cudax::__find_thread_block_size(__tile_total_size * sizeof(__value_type));
|
||||
|
||||
const auto __config = ::cuda::make_config(
|
||||
::cuda::block_dims(__thread_block_size),
|
||||
::cuda::grid_dims(__grid_size),
|
||||
::cuda::dynamic_shared_memory<__value_type[]>(__tile_total_size));
|
||||
|
||||
if (__tiling.__use_xor_swizzle)
|
||||
{
|
||||
const auto __kernel = cudax::__copy_shared_mem_kernel<
|
||||
true,
|
||||
decltype(__config),
|
||||
_MaxRank,
|
||||
_TpIn,
|
||||
_TpOut,
|
||||
_SrcAccessor,
|
||||
_DstAccessor,
|
||||
_ExtentT,
|
||||
_StrideTIn,
|
||||
_StrideTOut>;
|
||||
|
||||
::cuda::launch(
|
||||
__stream,
|
||||
__config,
|
||||
__kernel,
|
||||
__src.__data,
|
||||
__src_accessor,
|
||||
__dst.__data,
|
||||
__dst_accessor,
|
||||
__grid_iter,
|
||||
__grid_tile_src_strides,
|
||||
__grid_tile_dst_strides,
|
||||
__tile_perm_iter,
|
||||
__src_perm_src_strides,
|
||||
__tile_src_perm_smem_strides,
|
||||
__tile_dst_perm_iter,
|
||||
__dst_perm_dst_strides,
|
||||
__tile_dst_perm_smem_strides,
|
||||
__dst.__strides,
|
||||
static_cast<int>(__tile_total_size),
|
||||
__tile_sizes,
|
||||
__dst.__extents,
|
||||
__src.__strides);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto __kernel = cudax::__copy_shared_mem_kernel<
|
||||
false,
|
||||
decltype(__config),
|
||||
_MaxRank,
|
||||
_TpIn,
|
||||
_TpOut,
|
||||
_SrcAccessor,
|
||||
_DstAccessor,
|
||||
_ExtentT,
|
||||
_StrideTIn,
|
||||
_StrideTOut>;
|
||||
|
||||
::cuda::launch(
|
||||
__stream,
|
||||
__config,
|
||||
__kernel,
|
||||
__src.__data,
|
||||
__src_accessor,
|
||||
__dst.__data,
|
||||
__dst_accessor,
|
||||
__grid_iter,
|
||||
__grid_tile_src_strides,
|
||||
__grid_tile_dst_strides,
|
||||
__tile_perm_iter,
|
||||
__src_perm_src_strides,
|
||||
__tile_src_perm_smem_strides,
|
||||
__tile_dst_perm_iter,
|
||||
__dst_perm_dst_strides,
|
||||
__tile_dst_perm_smem_strides,
|
||||
__dst.__strides,
|
||||
static_cast<int>(__tile_total_size),
|
||||
__tile_sizes,
|
||||
__dst.__extents,
|
||||
__src.__strides);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
} // namespace cuda::experimental
|
||||
|
||||
#include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // _CUDAX__COPY_COPY_SHARED_MEMORY_H
|
||||
@@ -1,293 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_COPY_SHARED_MEMORY_UTILS_H
|
||||
#define _CUDAX__COPY_COPY_SHARED_MEMORY_UTILS_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
# include <cuda/__cmath/ceil_div.h>
|
||||
# include <cuda/__cmath/round_up.h>
|
||||
# include <cuda/__device/all_devices.h>
|
||||
# include <cuda/__device/attributes.h>
|
||||
# include <cuda/__device/device_ref.h>
|
||||
# include <cuda/__driver/driver_api.h>
|
||||
# include <cuda/std/__algorithm/min.h>
|
||||
# include <cuda/std/__cstddef/types.h>
|
||||
# include <cuda/std/array>
|
||||
|
||||
# include <cuda/experimental/__copy_bytes/tensor_query.cuh>
|
||||
# include <cuda/experimental/__copy_bytes/types.cuh>
|
||||
|
||||
# include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! Maximum tensor rank for which the shared-memory transpose kernel is instantiated. Higher ranks cause excessive
|
||||
//! register pressure (many rank-sized arrays and fully-unrolled loops).
|
||||
inline constexpr ::cuda::std::size_t __max_shared_mem_kernel_rank = 8;
|
||||
|
||||
//! A tile size is always representable by an unsigned integer.
|
||||
using __tile_extent_t = unsigned;
|
||||
|
||||
//! @brief Copy a raw tensor descriptor into one with a narrower static maximum rank.
|
||||
//!
|
||||
//! @param[in] __tensor Raw tensor descriptor with dynamic rank matching _RankOut
|
||||
//! @return Raw tensor descriptor with _RankOut as its static maximum rank
|
||||
template <::cuda::std::size_t _RankOut, typename _ExtentT, typename _StrideT, typename _Tp, ::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API __raw_tensor<_ExtentT, _StrideT, _Tp, _RankOut>
|
||||
__narrow_raw_tensor_rank(const __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>& __tensor) noexcept
|
||||
{
|
||||
_CCCL_ASSERT(__tensor.__rank == _RankOut, "tensor rank must match the narrowed static rank");
|
||||
__raw_tensor<_ExtentT, _StrideT, _Tp, _RankOut> __result{__tensor.__data, _RankOut, {}, {}};
|
||||
for (::cuda::std::size_t __i = 0; __i < _RankOut; ++__i)
|
||||
{
|
||||
__result.__extents[__i] = __tensor.__extents[__i];
|
||||
__result.__strides[__i] = __tensor.__strides[__i];
|
||||
}
|
||||
return __result;
|
||||
}
|
||||
|
||||
//! @brief Count the number of leading contiguous dimensions in a raw tensor.
|
||||
//!
|
||||
//! Starting from dimension 0, counts consecutive dimensions where `stride[0] == 1` and `stride[i] == stride[i-1] *
|
||||
//! extent[i-1]` for each subsequent dimension.
|
||||
//!
|
||||
//! @param[in] __tensor Raw tensor descriptor
|
||||
//! @return Number of leading contiguous dimensions (0 if stride[0] != 1 or rank is 0)
|
||||
template <typename _ExtentT, typename _StrideT, typename _Tp, ::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API ::cuda::std::size_t
|
||||
__num_contiguous_dimensions(const __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>& __tensor) noexcept
|
||||
{
|
||||
using __rank_t = typename __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>::__rank_t;
|
||||
if (__tensor.__rank == 0 || __tensor.__strides[0] != 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
__rank_t __count = 1;
|
||||
auto __expected_stride = static_cast<_StrideT>(__tensor.__extents[0]);
|
||||
for (__rank_t __i = 1; __i < __tensor.__rank; ++__i)
|
||||
{
|
||||
if (__tensor.__strides[__i] != __expected_stride)
|
||||
{
|
||||
break;
|
||||
}
|
||||
__expected_stride *= static_cast<_StrideT>(__tensor.__extents[__i]);
|
||||
++__count;
|
||||
}
|
||||
return __count;
|
||||
}
|
||||
|
||||
//! @brief Return a device_ref for the current CUDA device.
|
||||
//!
|
||||
//! @return Device reference for the active CUDA context's device
|
||||
[[nodiscard]] _CCCL_HOST_API inline ::cuda::device_ref __current_device() noexcept
|
||||
{
|
||||
const auto __dev_id = ::cuda::__driver::__cudevice_to_ordinal(::cuda::__driver::__ctxGetDevice());
|
||||
return ::cuda::devices[__dev_id];
|
||||
}
|
||||
|
||||
//! Maximum extent of a single tile dimension, set to the warp size so that the innermost tile dimension maps to a
|
||||
//! full warp of coalesced accesses.
|
||||
inline constexpr size_t __max_tile_size = 32;
|
||||
|
||||
// The structure holds the tiling information to optimize the transpose (shared-memory) kernel.
|
||||
// - __tile_sizes: the size of each tile dimension in shared-memory
|
||||
// - __src_perm: the permutation of the source dimensions (copy to shared-memory)
|
||||
// - __dst_perm: the permutation of the destination dimensions (copy from shared-memory)
|
||||
// - __tile_total_size: the total size of the tile in shared-memory
|
||||
// - __active_tile_dims: the number of dimensions covered by the tile
|
||||
// - __active_32_dims: the number of tile dimensions with extent __max_tile_size
|
||||
// - __is_valid: true if the tiling is valid
|
||||
// - __use_xor_swizzle: true if the XOR swizzle is used
|
||||
template <::cuda::std::size_t _MaxRank>
|
||||
struct __shared_mem_tiling_result
|
||||
{
|
||||
::cuda::std::array<__tile_extent_t, _MaxRank> __tile_sizes{};
|
||||
::cuda::std::array<::cuda::std::size_t, _MaxRank> __src_perm{};
|
||||
::cuda::std::array<::cuda::std::size_t, _MaxRank> __dst_perm{};
|
||||
::cuda::std::size_t __tile_total_size = 1;
|
||||
::cuda::std::size_t __active_tile_dims = 0;
|
||||
::cuda::std::size_t __active_32_dims = 0;
|
||||
bool __is_valid = false;
|
||||
bool __use_xor_swizzle = false;
|
||||
};
|
||||
|
||||
//! @brief Adds a contiguous stride-1 run from one tensor to the shared-memory tile.
|
||||
//!
|
||||
//! @param[in] __tensor Raw tensor descriptor used to find coalesced modes
|
||||
//! @param[in] __perm Mode order to scan
|
||||
//! @param[in,out] __result Shared-memory tiling result updated with selected tile sizes
|
||||
//! @param[in] __max_shared_mem_bytes Maximum shared-memory capacity for one tile
|
||||
//! @return Number of coalesced elements covered by this tensor's selected tile run
|
||||
template <typename _SmemTp, typename _ExtentT, typename _StrideT, typename _Tp, ::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API ::cuda::std::size_t __add_coalesced_tile_run(
|
||||
const __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>& __tensor,
|
||||
const ::cuda::std::array<::cuda::std::size_t, _MaxRank>& __perm,
|
||||
__shared_mem_tiling_result<_MaxRank>& __result,
|
||||
::cuda::std::size_t __max_shared_mem_bytes) noexcept
|
||||
{
|
||||
using ::cuda::std::size_t;
|
||||
size_t __coalesced_tile_size = 1;
|
||||
_StrideT __expected_stride = 1;
|
||||
|
||||
for (size_t __i = 0; __i < __tensor.__rank; ++__i)
|
||||
{
|
||||
const auto __perm_i = __perm[__i];
|
||||
const auto __extent = static_cast<size_t>(__tensor.__extents[__perm_i]);
|
||||
const auto __stride = ::cuda::experimental::__abs_integer(__tensor.__strides[__perm_i]);
|
||||
if (__stride != __expected_stride) // input tensor not contiguous
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (__result.__tile_sizes[__perm_i] == 1) // first time we see this dimension
|
||||
{
|
||||
const auto __tile_size = ::cuda::std::min(__extent, __max_tile_size);
|
||||
const auto __tile_total_size_bytes = __result.__tile_total_size * __tile_size * sizeof(_SmemTp);
|
||||
if (__tile_total_size_bytes > __max_shared_mem_bytes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// if the tile fits in shared-memory, update the result
|
||||
__result.__tile_sizes[__perm_i] = static_cast<__tile_extent_t>(__tile_size);
|
||||
__result.__tile_total_size *= __tile_size;
|
||||
++__result.__active_tile_dims;
|
||||
if (__tile_size == __max_tile_size)
|
||||
{
|
||||
++__result.__active_32_dims;
|
||||
}
|
||||
}
|
||||
__coalesced_tile_size *= __result.__tile_sizes[__perm_i];
|
||||
__expected_stride *= static_cast<_StrideT>(__extent);
|
||||
}
|
||||
return __coalesced_tile_size;
|
||||
}
|
||||
|
||||
//! @brief Compute a source/destination-aware shared-memory tile.
|
||||
//!
|
||||
//! The selected tile spans coalesced dimensions from both layouts. This keeps the load phase ordered by source stride
|
||||
//! and the store phase ordered by destination stride, without requiring either coalesced dimension to be mode 0.
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor descriptor
|
||||
//! @param[in] __dst Destination raw tensor descriptor
|
||||
//! @return Shared-memory tiling decision and layout permutations
|
||||
template <typename _TpIn,
|
||||
typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _TpSrc,
|
||||
typename _StrideTOut,
|
||||
typename _TpDst,
|
||||
::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API __shared_mem_tiling_result<_MaxRank>
|
||||
__find_shared_mem_tiling(const __raw_tensor<_ExtentT, _StrideTIn, _TpSrc, _MaxRank>& __src,
|
||||
const __raw_tensor<_ExtentT, _StrideTOut, _TpDst, _MaxRank>& __dst) noexcept
|
||||
{
|
||||
using ::cuda::std::size_t;
|
||||
__shared_mem_tiling_result<_MaxRank> __result{};
|
||||
// initialize the source and destination permutations and sort them by stride
|
||||
for (size_t __i = 0; __i < _MaxRank; ++__i)
|
||||
{
|
||||
__result.__tile_sizes[__i] = 1;
|
||||
__result.__src_perm[__i] = __i;
|
||||
__result.__dst_perm[__i] = __i;
|
||||
}
|
||||
__result.__src_perm = ::cuda::experimental::__stride_order(__src);
|
||||
__result.__dst_perm = ::cuda::experimental::__stride_order(__dst);
|
||||
|
||||
const auto __current_dev = ::cuda::experimental::__current_device();
|
||||
const size_t __max_shared_mem_bytes = __current_dev.attribute<::cudaDevAttrMaxSharedMemoryPerBlock>();
|
||||
const auto __src_coalesced_tile_size =
|
||||
::cuda::experimental::__add_coalesced_tile_run<_TpIn>(__src, __result.__src_perm, __result, __max_shared_mem_bytes);
|
||||
const auto __dst_coalesced_tile_size =
|
||||
::cuda::experimental::__add_coalesced_tile_run<_TpIn>(__dst, __result.__dst_perm, __result, __max_shared_mem_bytes);
|
||||
|
||||
// If the tile total size is too small, or the coalescing is not useful on both sides, or the number of active tile
|
||||
// dimensions is less than 2, return the result.
|
||||
if (__result.__tile_total_size < __max_tile_size * 8 || __src_coalesced_tile_size < 2 || __dst_coalesced_tile_size < 2
|
||||
|| __result.__active_tile_dims < 2)
|
||||
{
|
||||
return __result;
|
||||
}
|
||||
|
||||
// There must be enough blocks to keep the GPU busy (at least one full wave across all SMs).
|
||||
const size_t __num_sms = __current_dev.attribute<::cudaDevAttrMultiProcessorCount>();
|
||||
size_t __num_tiles = 1;
|
||||
for (size_t __r = 0; __r < __dst.__rank; ++__r)
|
||||
{
|
||||
const auto __extent = static_cast<size_t>(__dst.__extents[__r]);
|
||||
const auto __tile_size = static_cast<size_t>(__result.__tile_sizes[__r]);
|
||||
__num_tiles *= ::cuda::ceil_div(__extent, __tile_size);
|
||||
}
|
||||
if (__num_tiles < __num_sms)
|
||||
{
|
||||
return __result;
|
||||
}
|
||||
|
||||
__result.__is_valid = true;
|
||||
// Shared memory swizzle makes sense only for 32-bit and 64-bit types.
|
||||
__result.__use_xor_swizzle = (sizeof(_TpIn) == 4 || sizeof(_TpIn) == 8) //
|
||||
&& __result.__active_32_dims == 2;
|
||||
return __result;
|
||||
}
|
||||
|
||||
//! @brief Decide whether the shared-memory tiled transpose kernel is profitable.
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor descriptor
|
||||
//! @param[in] __dst Destination raw tensor descriptor
|
||||
//! @return true if the shared-memory kernel should be used
|
||||
template <typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
typename _TpIn,
|
||||
typename _TpOut,
|
||||
::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API bool
|
||||
__use_shared_mem_kernel(const __raw_tensor<_ExtentT, _StrideTIn, _TpIn, _MaxRank>& __src,
|
||||
const __raw_tensor<_ExtentT, _StrideTOut, _TpOut, _MaxRank>& __dst) noexcept
|
||||
{
|
||||
return ::cuda::experimental::__find_shared_mem_tiling<_TpIn>(__src, __dst).__is_valid;
|
||||
}
|
||||
|
||||
//! @brief Compute the thread block size for the shared-memory kernel.
|
||||
//!
|
||||
//! Balances occupancy by dividing the SM threads across as many blocks as the shared memory allows, then caps at
|
||||
//! the device maximum.
|
||||
//!
|
||||
//! @param[in] __tile_total_bytes Shared memory required for one tile in bytes
|
||||
//! @return Thread block size
|
||||
[[nodiscard]] _CCCL_HOST_API inline int __find_thread_block_size(::cuda::std::size_t __tile_total_bytes) noexcept
|
||||
{
|
||||
using ::cuda::std::size_t;
|
||||
const auto __dev = ::cuda::experimental::__current_device();
|
||||
const size_t __total_sm_threads = __dev.attribute<::cudaDevAttrMaxThreadsPerMultiProcessor>();
|
||||
const size_t __max_thread_block_size = __dev.attribute<::cudaDevAttrMaxThreadsPerBlock>();
|
||||
const size_t __total_shared_mem_bytes = __dev.attribute<::cudaDevAttrMaxSharedMemoryPerMultiprocessor>();
|
||||
const auto __num_blocks_per_sm = __total_shared_mem_bytes / __tile_total_bytes;
|
||||
const auto __thread_block_size = ::cuda::std::min(__total_sm_threads / __num_blocks_per_sm, __max_thread_block_size);
|
||||
const auto __thread_block_size32 = ::cuda::round_up(__thread_block_size, /*warp size=*/size_t{32});
|
||||
return static_cast<int>(__thread_block_size32);
|
||||
}
|
||||
} // namespace cuda::experimental
|
||||
|
||||
# include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
#endif // _CUDAX__COPY_COPY_SHARED_MEMORY_UTILS_H
|
||||
@@ -1,149 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_DISPATCH_BY_VECTOR_H
|
||||
#define _CUDAX__COPY_DISPATCH_BY_VECTOR_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
# include <cuda/std/__algorithm/min.h>
|
||||
# include <cuda/std/__cstddef/types.h>
|
||||
# include <cuda/std/__type_traits/integral_constant.h>
|
||||
|
||||
# include <cuda/experimental/__copy/tensor_copy_utils.cuh>
|
||||
# include <cuda/experimental/__copy_bytes/types.cuh>
|
||||
|
||||
# include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Compute the maximum vector access width in bytes for a pair of raw tensors.
|
||||
//!
|
||||
//! Takes the minimum of the source alignment, destination alignment, and the GPU architecture's
|
||||
//! maximum vector width.
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor
|
||||
//! @param[in] __dst Destination raw tensor
|
||||
//! @return Maximum safe vector access width in bytes
|
||||
template <typename _SrcExtentT,
|
||||
typename _SrcStrideT,
|
||||
typename _TpSrc,
|
||||
typename _DstExtentT,
|
||||
typename _DstStrideT,
|
||||
typename _TpDst,
|
||||
::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API ::cuda::std::size_t
|
||||
__vector_size_bytes(const __raw_tensor<_SrcExtentT, _SrcStrideT, _TpSrc, _MaxRank>& __src,
|
||||
const __raw_tensor<_DstExtentT, _DstStrideT, _TpDst, _MaxRank>& __dst) noexcept
|
||||
{
|
||||
return ::cuda::std::min(
|
||||
{::cuda::experimental::__max_alignment(__src),
|
||||
::cuda::experimental::__max_alignment(__dst),
|
||||
::cuda::experimental::__max_gpu_arch_vector_size()});
|
||||
}
|
||||
|
||||
template <int _VectorSize>
|
||||
constexpr auto __const_vector_size = ::cuda::std::integral_constant<int, _VectorSize>{};
|
||||
|
||||
//! @brief Dispatch a copy operation with the optimal vectorized element type.
|
||||
//!
|
||||
//! Computes the maximum safe vector width from the source and destination tensors, reshapes both
|
||||
//! tensors to that vector type via @ref __reshape_vectorized, and invokes @p __op with the reshaped tensors.
|
||||
//!
|
||||
//! @param[in] __src Source raw tensor descriptor
|
||||
//! @param[in] __dst Destination raw tensor descriptor
|
||||
//! @param[in] __op Callable invoked with the reshaped source and destination tensors
|
||||
template <typename _ExtentT,
|
||||
typename _StrideTIn,
|
||||
typename _StrideTOut,
|
||||
typename _TpIn,
|
||||
typename _TpOut,
|
||||
::cuda::std::size_t _Rank,
|
||||
typename _Op>
|
||||
_CCCL_HOST_API void __dispatch_by_vector_size(
|
||||
const __raw_tensor<_ExtentT, _StrideTIn, _TpIn, _Rank>& __src,
|
||||
const __raw_tensor<_ExtentT, _StrideTOut, _TpOut, _Rank>& __dst,
|
||||
_Op __op) noexcept
|
||||
{
|
||||
namespace cudax = ::cuda::experimental;
|
||||
const auto __call_vectorized = [&](auto __const_vector_size) {
|
||||
const auto __src_recast = cudax::__reshape_vectorized<__const_vector_size>(__src);
|
||||
const auto __dst_recast = cudax::__reshape_vectorized<__const_vector_size>(__dst);
|
||||
__op(__src_recast, __dst_recast);
|
||||
};
|
||||
const auto __vector_size_bytes = cudax::__vector_size_bytes(__src, __dst);
|
||||
// 32-bytes aligned vector types have been introduced in CTK 13.0
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
static_assert(sizeof(_TpIn) <= 32);
|
||||
if constexpr (sizeof(_TpIn) <= 32)
|
||||
{
|
||||
if (__vector_size_bytes == 32)
|
||||
{
|
||||
__call_vectorized(__const_vector_size<32>);
|
||||
return;
|
||||
}
|
||||
}
|
||||
# else // ^^^ _CCCL_CTK_AT_LEAST(13, 0) ^^^ / vvv _CCCL_CTK_BELOW(13, 0) vvv
|
||||
static_assert(sizeof(_TpIn) <= 16);
|
||||
# endif // _CCCL_CTK_AT_LEAST(13, 0)
|
||||
if constexpr (sizeof(_TpIn) <= 16)
|
||||
{
|
||||
if (__vector_size_bytes == 16)
|
||||
{
|
||||
__call_vectorized(__const_vector_size<16>);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (sizeof(_TpIn) <= 8)
|
||||
{
|
||||
if (__vector_size_bytes == 8)
|
||||
{
|
||||
__call_vectorized(__const_vector_size<8>);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (sizeof(_TpIn) <= 4)
|
||||
{
|
||||
if (__vector_size_bytes == 4)
|
||||
{
|
||||
__call_vectorized(__const_vector_size<4>);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (sizeof(_TpIn) <= 2)
|
||||
{
|
||||
if (__vector_size_bytes == 2)
|
||||
{
|
||||
__call_vectorized(__const_vector_size<2>);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (sizeof(_TpIn) <= 1)
|
||||
{
|
||||
__call_vectorized(__const_vector_size<1>);
|
||||
}
|
||||
// no fallthrough (sizeof(T) is never 0)
|
||||
}
|
||||
} // namespace cuda::experimental
|
||||
|
||||
# include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
#endif // _CUDAX__COPY_DISPATCH_BY_VECTOR_H
|
||||
@@ -1,266 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_MDSPAN_D2D_H
|
||||
#define _CUDAX__COPY_MDSPAN_D2D_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#include <cuda/std/__type_traits/remove_cv.h>
|
||||
|
||||
#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
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
# include <cub/device/device_transform.cuh>
|
||||
|
||||
# include <cuda/__cmath/pow2.h>
|
||||
# include <cuda/__driver/driver_api.h>
|
||||
# include <cuda/__functional/address_stability.h>
|
||||
# include <cuda/__mdspan/host_device_mdspan.h>
|
||||
# include <cuda/__mdspan/traits.h>
|
||||
# include <cuda/__stream/stream_ref.h>
|
||||
# include <cuda/__type_traits/is_trivially_copyable.h>
|
||||
# include <cuda/std/__algorithm/max.h>
|
||||
# include <cuda/std/__functional/identity.h>
|
||||
# include <cuda/std/__host_stdlib/stdexcept>
|
||||
# include <cuda/std/__mdspan/default_accessor.h>
|
||||
# include <cuda/std/__memory/is_sufficiently_aligned.h>
|
||||
# include <cuda/std/__type_traits/common_type.h>
|
||||
# include <cuda/std/__type_traits/conditional.h>
|
||||
# include <cuda/std/__type_traits/is_const.h>
|
||||
# include <cuda/std/__type_traits/is_convertible.h>
|
||||
# include <cuda/std/__type_traits/is_same.h>
|
||||
|
||||
# include <cuda/experimental/__copy/copy_contiguous.cuh>
|
||||
# include <cuda/experimental/__copy/copy_optimized.cuh>
|
||||
# include <cuda/experimental/__copy/copy_shared_memory.cuh>
|
||||
# include <cuda/experimental/__copy/dispatch_by_vector.cuh>
|
||||
# include <cuda/experimental/__copy/tensor_copy_utils.cuh>
|
||||
# include <cuda/experimental/__copy/vector_access.cuh>
|
||||
# include <cuda/experimental/__copy_bytes/simplify_paired.cuh>
|
||||
# include <cuda/experimental/__copy_bytes/tensor_query.cuh>
|
||||
|
||||
# include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Copy elements between two device mdspans.
|
||||
//!
|
||||
//! Validates preconditions, converts mdspans to raw tensor descriptors, simplifies the paired layout
|
||||
//! (sort, flip negative strides, coalesce), then dispatches either a vectorized contiguous kernel or a
|
||||
//! strided element-wise kernel.
|
||||
//!
|
||||
//! @param[in] __src Source device mdspan
|
||||
//! @param[out] __dst Destination device mdspan
|
||||
//! @param[in] __stream CUDA stream for the asynchronous transfer
|
||||
template <typename _TpIn,
|
||||
typename _ExtentsIn,
|
||||
typename _LayoutPolicyIn,
|
||||
typename _AccessorPolicyIn,
|
||||
typename _TpOut,
|
||||
typename _ExtentsOut,
|
||||
typename _LayoutPolicyOut,
|
||||
typename _AccessorPolicyOut>
|
||||
_CCCL_HOST_API void copy(::cuda::device_mdspan<_TpIn, _ExtentsIn, _LayoutPolicyIn, _AccessorPolicyIn> __src,
|
||||
::cuda::device_mdspan<_TpOut, _ExtentsOut, _LayoutPolicyOut, _AccessorPolicyOut> __dst,
|
||||
::cuda::stream_ref __stream)
|
||||
{
|
||||
namespace cudax = ::cuda::experimental;
|
||||
static_assert(::cuda::std::is_convertible_v<_TpIn, _TpOut>, "TpIn must be convertible to TpOut");
|
||||
static_assert(!::cuda::std::is_const_v<_TpOut>, "TpOut must not be const");
|
||||
static_assert(::cuda::__is_cuda_mdspan_layout_v<_LayoutPolicyIn>,
|
||||
"LayoutPolicyIn must be a predefined layout policy");
|
||||
static_assert(::cuda::__is_cuda_mdspan_layout_v<_LayoutPolicyOut>,
|
||||
"LayoutPolicyOut must be a predefined layout policy");
|
||||
|
||||
if (__src.size() != __dst.size())
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "mdspans must have the same size");
|
||||
}
|
||||
const auto __tensor_size = __src.size();
|
||||
if (__tensor_size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (__src.data_handle() == nullptr || __dst.data_handle() == nullptr)
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "mdspan data handle must not be nullptr");
|
||||
}
|
||||
if (!::cuda::std::is_sufficiently_aligned<alignof(_TpIn)>(__src.data_handle()))
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "source mdspan must be sufficiently aligned");
|
||||
}
|
||||
if (!::cuda::std::is_sufficiently_aligned<alignof(_TpOut)>(__dst.data_handle()))
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "destination mdspan must be sufficiently aligned");
|
||||
}
|
||||
if (cudax::__has_interleaved_stride_order(__dst))
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "destination mdspan must not have interleaved stride order");
|
||||
}
|
||||
if (cudax::__may_overlap(__src, __dst))
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "mdspans must not overlap in memory");
|
||||
}
|
||||
|
||||
using __default_accessor_in = ::cuda::std::default_accessor<_TpIn>;
|
||||
using __default_accessor_out = ::cuda::std::default_accessor<_TpOut>;
|
||||
constexpr bool __have_default_accessors =
|
||||
::cuda::std::is_convertible_v<_AccessorPolicyIn, __default_accessor_in>
|
||||
&& ::cuda::std::is_convertible_v<_AccessorPolicyOut, __default_accessor_out>;
|
||||
constexpr bool __are_byte_copyable =
|
||||
::cuda::std::is_same_v<::cuda::std::remove_cv_t<_TpIn>, ::cuda::std::remove_cv_t<_TpOut>>
|
||||
&& ::cuda::is_trivially_copyable_v<_TpIn> //
|
||||
&& __have_default_accessors;
|
||||
|
||||
if (__tensor_size == 1 && __are_byte_copyable)
|
||||
{
|
||||
auto __src_ptr = __src.data_handle();
|
||||
auto __dst_ptr = __dst.data_handle();
|
||||
if constexpr (::cuda::__is_layout_stride_relaxed_v<_LayoutPolicyIn>)
|
||||
{
|
||||
__src_ptr += __src.mapping().offset();
|
||||
}
|
||||
if constexpr (::cuda::__is_layout_stride_relaxed_v<_LayoutPolicyOut>)
|
||||
{
|
||||
__dst_ptr += __dst.mapping().offset();
|
||||
}
|
||||
::cuda::__driver::__memcpyAsync(__dst_ptr, __src_ptr, sizeof(_TpIn), __stream.get());
|
||||
return;
|
||||
}
|
||||
|
||||
// rank == 0 for both tensors is already handled above -> their size is exactly 1
|
||||
if constexpr (_ExtentsIn::rank() > 0 && _ExtentsOut::rank() > 0)
|
||||
{
|
||||
// use the most efficient type for device code
|
||||
using __src_extent_t = ::cuda::std::common_type_t<typename _ExtentsIn::index_type, int>;
|
||||
using __dst_extent_t = ::cuda::std::common_type_t<typename _ExtentsOut::index_type, int>;
|
||||
using __common_extent_t =
|
||||
::cuda::std::conditional_t<(sizeof(__src_extent_t) < sizeof(__dst_extent_t)), __src_extent_t, __dst_extent_t>;
|
||||
using __src_stride_t =
|
||||
::cuda::std::common_type_t<cudax::__mdspan_stride_t<_LayoutPolicyIn, decltype(__src.mapping())>, int>;
|
||||
using __dst_stride_t =
|
||||
::cuda::std::common_type_t<cudax::__mdspan_stride_t<_LayoutPolicyOut, decltype(__dst.mapping())>, int>;
|
||||
|
||||
constexpr auto __max_rank = ::cuda::std::max(_ExtentsIn::rank(), _ExtentsOut::rank());
|
||||
const auto __src_raw = cudax::__to_raw_tensor<__common_extent_t, __src_stride_t, __max_rank>(__src);
|
||||
const auto __dst_raw = cudax::__to_raw_tensor<__common_extent_t, __dst_stride_t, __max_rank>(__dst);
|
||||
if (!cudax::__same_extents(__src_raw, __dst_raw))
|
||||
{
|
||||
_CCCL_THROW(::std::invalid_argument, "mdspans must have the same extents (after removing singleton dimensions)");
|
||||
}
|
||||
|
||||
auto __src_simplified = __src_raw;
|
||||
auto __dst_simplified = __dst_raw;
|
||||
cudax::__sort_by_stride_paired(__src_simplified, __dst_simplified);
|
||||
cudax::__flip_negative_strides_paired(__src_simplified, __dst_simplified);
|
||||
cudax::__coalesce_paired(__src_simplified, __dst_simplified);
|
||||
const bool __both_stride1 = (__src_simplified.__strides[0] == 1) && (__dst_simplified.__strides[0] == 1);
|
||||
const auto __tile_size = __both_stride1 ? __src_simplified.__extents[0] : 1;
|
||||
const auto __src_normalized = (__tile_size > 1) ? __src_simplified : cudax::__reverse_modes(__src_raw);
|
||||
const auto __dst_normalized = (__tile_size > 1) ? __dst_simplified : cudax::__reverse_modes(__dst_raw);
|
||||
|
||||
_CCCL_ASSERT(__tensor_size % __tile_size == 0, "tensor size must be divisible by tile size");
|
||||
const auto __inner_extent_bytes = __src_normalized.__extents[0] * sizeof(_TpIn);
|
||||
|
||||
// check the preconditions for the vectorized case
|
||||
constexpr bool __are_vectorizable_copy =
|
||||
sizeof(_TpIn) <= __max_vector_access && ::cuda::is_power_of_two(sizeof(_TpIn)) && __are_byte_copyable;
|
||||
|
||||
// (1) contiguous case
|
||||
if constexpr (__have_default_accessors)
|
||||
{
|
||||
if (static_cast<::cuda::std::size_t>(__tile_size) == __tensor_size)
|
||||
{
|
||||
_CCCL_TRY_CUDA_API(
|
||||
CUB_NS_QUALIFIER::DeviceTransform::Transform,
|
||||
"cub::DeviceTransform::Transform failed",
|
||||
__src_simplified.__data,
|
||||
__dst_simplified.__data,
|
||||
__tensor_size,
|
||||
::cuda::proclaim_copyable_arguments(::cuda::std::identity{}),
|
||||
__stream.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// (2) inner size is large
|
||||
if (__both_stride1 && __inner_extent_bytes >= cudax::__bytes_in_flight())
|
||||
{
|
||||
// (2a) vectorized case
|
||||
if constexpr (__are_vectorizable_copy)
|
||||
{
|
||||
const auto __op = [__stream](const auto& __src, const auto& __dst) {
|
||||
cudax::__launch_copy_contiguous_kernel(__src, __dst, __stream);
|
||||
};
|
||||
cudax::__dispatch_by_vector_size(__src_normalized, __dst_normalized, __op);
|
||||
}
|
||||
// (2b) non-vectorized case but inner size is large enough to use the contiguous kernel
|
||||
else
|
||||
{
|
||||
cudax::__launch_copy_contiguous_kernel(
|
||||
__src_normalized, __dst_normalized, __stream, __src.accessor(), __dst.accessor());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// (3) inner size is not large -> try vectorized case
|
||||
if constexpr (__are_vectorizable_copy)
|
||||
{
|
||||
if (__both_stride1)
|
||||
{
|
||||
const auto __op = [__stream](const auto& __src, const auto& __dst) {
|
||||
cudax::__copy_optimized(__src, __dst, cudax::__total_size(__src), __stream);
|
||||
};
|
||||
cudax::__dispatch_by_vector_size(__src_normalized, __dst_normalized, __op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// (4) transpose case (rank capped to avoid excessive register pressure in the kernel)
|
||||
if constexpr (__max_rank >= 2 && __max_rank <= cudax::__max_shared_mem_kernel_rank)
|
||||
{
|
||||
if (__src_simplified.__rank == 2) // Optimize when the actual rank is 2
|
||||
{
|
||||
const auto __src_rank2 = cudax::__narrow_raw_tensor_rank<2>(__src_simplified);
|
||||
const auto __dst_rank2 = cudax::__narrow_raw_tensor_rank<2>(__dst_simplified);
|
||||
if (cudax::__use_shared_mem_kernel(__src_rank2, __dst_rank2))
|
||||
{
|
||||
cudax::__launch_copy_shared_mem_kernel(__src_rank2, __dst_rank2, __stream, __src.accessor(), __dst.accessor());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cudax::__use_shared_mem_kernel(__src_simplified, __dst_simplified))
|
||||
{
|
||||
cudax::__launch_copy_shared_mem_kernel(
|
||||
__src_simplified, __dst_simplified, __stream, __src.accessor(), __dst.accessor());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// (5) generic case (fallback)
|
||||
cudax::__copy_optimized(
|
||||
__src_normalized,
|
||||
__dst_normalized,
|
||||
cudax::__total_size(__src_normalized),
|
||||
__stream,
|
||||
__src.accessor(),
|
||||
__dst.accessor());
|
||||
}
|
||||
}
|
||||
} // namespace cuda::experimental
|
||||
|
||||
# include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
#endif // _CUDAX__COPY_MDSPAN_D2D_H
|
||||
@@ -1,192 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_TENSOR_COPY_UTILS_H
|
||||
#define _CUDAX__COPY_TENSOR_COPY_UTILS_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
# include <cuda/__memory/ptr_alignment.h>
|
||||
# include <cuda/__memory/ranges_overlap.h>
|
||||
# include <cuda/__utility/in_range.h>
|
||||
# include <cuda/std/__cstddef/types.h>
|
||||
# include <cuda/std/__mdspan/mdspan.h>
|
||||
# include <cuda/std/__memory/is_sufficiently_aligned.h>
|
||||
# include <cuda/std/__numeric/gcd_lcm.h>
|
||||
# include <cuda/std/__type_traits/conditional.h>
|
||||
# include <cuda/std/__type_traits/is_const.h>
|
||||
|
||||
# include <cuda/experimental/__copy/vector_access.cuh>
|
||||
# include <cuda/experimental/__copy_bytes/abs_integer.cuh>
|
||||
# include <cuda/experimental/__copy_bytes/types.cuh>
|
||||
|
||||
# include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Compute the maximum vectorization width in bytes for a raw tensor.
|
||||
//!
|
||||
//! Expects mode 0 to be the contiguous mode (stride == 1), as established by
|
||||
//! @ref __sort_by_stride_paired. Computes the largest power-of-two vector width such that:
|
||||
//! - The pointer is aligned to that width.
|
||||
//! - All non-contiguous strides (in bytes) are divisible by it.
|
||||
//! - The contiguous mode's shape is divisible by the element count.
|
||||
//! The result is capped at 16 bytes. If mode 0 is not contiguous, returns sizeof(_Tp).
|
||||
//!
|
||||
//! @pre `__tensor.__rank` is in [1, _MaxRank].
|
||||
//! @pre All shapes must be > 1 (no degenerate modes).
|
||||
//! @pre Strides are sorted by @ref __sort_by_stride_paired (mode 0 has the smallest absolute stride).
|
||||
//!
|
||||
//! @param[in] __tensor Raw tensor with strides sorted by @ref __sort_by_stride_paired
|
||||
//! @return Maximum safe vectorization width in bytes, in [sizeof(_Tp), 16]
|
||||
template <typename _ExtentT, typename _StrideT, typename _Tp, ::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]] _CCCL_HOST_API ::cuda::std::size_t
|
||||
__max_alignment(const __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>& __tensor) noexcept
|
||||
{
|
||||
using ::cuda::std::size_t;
|
||||
using __raw_tensor_t = __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>;
|
||||
using __rank_t = typename __raw_tensor_t::__rank_t;
|
||||
_CCCL_ASSERT(::cuda::in_range(__tensor.__rank, size_t{1}, _MaxRank), "Invalid tensor rank");
|
||||
if (__tensor.__strides[0] != 1)
|
||||
{
|
||||
return sizeof(_Tp);
|
||||
}
|
||||
// (1) pointer alignment
|
||||
size_t __alignment = ::cuda::__ptr_alignment(__tensor.__data);
|
||||
// (2) alignment over all strides
|
||||
for (__rank_t __i = 0; __i < __tensor.__rank; ++__i)
|
||||
{
|
||||
const auto __stride = ::cuda::experimental::__abs_integer(__tensor.__strides[__i]);
|
||||
if (__stride != 1)
|
||||
{
|
||||
const size_t __stride_bytes = static_cast<size_t>(__stride) * sizeof(_Tp);
|
||||
__alignment = ::cuda::std::gcd(__alignment, __stride_bytes);
|
||||
}
|
||||
}
|
||||
_CCCL_ASSERT(__alignment % sizeof(_Tp) == 0, "alignment is not a multiple of the element size");
|
||||
// (3) Compute the number of items per vector over the contiguous mode
|
||||
size_t __elem_alignment = __alignment / sizeof(_Tp);
|
||||
__elem_alignment = ::cuda::std::gcd(__elem_alignment, static_cast<size_t>(__tensor.__extents[0]));
|
||||
return __elem_alignment * sizeof(_Tp);
|
||||
}
|
||||
|
||||
template <::cuda::std::size_t _VectorBytes, typename _Tp>
|
||||
using __reshape_vector_type =
|
||||
::cuda::std::conditional_t<::cuda::std::is_const_v<_Tp>,
|
||||
const ::cuda::experimental::__vector_access_t<_VectorBytes>,
|
||||
::cuda::experimental::__vector_access_t<_VectorBytes>>;
|
||||
|
||||
//! @brief Reshape a raw tensor for vectorized access by widening the element type.
|
||||
//!
|
||||
//! @pre Mode 0 must be contiguous (stride == 1).
|
||||
//! @pre The innermost extent (in bytes) must be divisible by @p _VectorBytes.
|
||||
//! @pre All non-innermost strides must be divisible by the elements-per-vector ratio.
|
||||
//!
|
||||
//! @tparam _VectorBytes Target vector width in bytes
|
||||
//! @param[in] __tensor Raw tensor with contiguous innermost mode
|
||||
//! @return Raw tensor with element type replaced by the vector type and adjusted extents/strides
|
||||
template <::cuda::std::size_t _VectorBytes, typename _ExtentT, typename _StrideT, typename _Tp, ::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]]
|
||||
_CCCL_HOST_API __raw_tensor<_ExtentT, _StrideT, __reshape_vector_type<_VectorBytes, _Tp>, _MaxRank>
|
||||
__reshape_vectorized(const __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>& __tensor) noexcept
|
||||
{
|
||||
using __vector_t = __reshape_vector_type<_VectorBytes, _Tp>;
|
||||
using __rank_t = typename __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>::__rank_t;
|
||||
static_assert(_VectorBytes % sizeof(_Tp) == 0, "vector size must be a multiple of element size");
|
||||
|
||||
constexpr auto __elems_per_vector = _VectorBytes / sizeof(_Tp);
|
||||
_CCCL_ASSERT(__tensor.__strides[0] == 1, "innermost mode must be contiguous");
|
||||
_CCCL_ASSERT(__tensor.__extents[0] % __elems_per_vector == 0,
|
||||
"innermost extent must be divisible by elements per vector");
|
||||
_CCCL_ASSERT(::cuda::std::is_sufficiently_aligned<alignof(__vector_t)>(__tensor.__data),
|
||||
"tensor data is not sufficiently aligned to the extents and strides");
|
||||
|
||||
const auto __data = reinterpret_cast<__vector_t*>(__tensor.__data);
|
||||
__raw_tensor<_ExtentT, _StrideT, __vector_t, _MaxRank> __result{
|
||||
__data, __tensor.__rank, __tensor.__extents, __tensor.__strides};
|
||||
__result.__extents[0] /= __elems_per_vector;
|
||||
|
||||
for (__rank_t __i = 1; __i < __result.__rank; ++__i)
|
||||
{
|
||||
_CCCL_ASSERT(__result.__strides[__i] % _StrideT{__elems_per_vector} == 0,
|
||||
"non-innermost strides must be divisible by elements per vector");
|
||||
__result.__strides[__i] /= _StrideT{__elems_per_vector};
|
||||
}
|
||||
return __result;
|
||||
}
|
||||
|
||||
//! @brief Compute the total number of elements in a raw tensor.
|
||||
//!
|
||||
//! @param[in] __tensor Raw tensor descriptor
|
||||
//! @return Product of all extents
|
||||
template <typename _ExtentT, typename _StrideT, typename _Tp, ::cuda::std::size_t _MaxRank>
|
||||
[[nodiscard]]
|
||||
_CCCL_HOST_API _ExtentT __total_size(const __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>& __tensor) noexcept
|
||||
{
|
||||
using __raw_tensor_t = __raw_tensor<_ExtentT, _StrideT, _Tp, _MaxRank>;
|
||||
using __rank_t = typename __raw_tensor_t::__rank_t;
|
||||
_ExtentT __total_size = 1;
|
||||
for (__rank_t __i = 0; __i < __tensor.__rank; ++__i)
|
||||
{
|
||||
__total_size *= __tensor.__extents[__i];
|
||||
}
|
||||
return __total_size;
|
||||
}
|
||||
|
||||
//! @brief Conservative check whether two mdspans may access overlapping memory.
|
||||
//!
|
||||
//! Uses each mapping's @c required_span_size() to compute the half-open byte range
|
||||
//! @c [data_handle, data_handle + required_span_size * sizeof(T)) and checks for intersection.
|
||||
//! NOTE: the function doesn't check strict overlap for non-contiguous layouts, for example, the padding could be
|
||||
//! between the two mdspans.
|
||||
//!
|
||||
//! Empty mdspans (size == 0) are considered non-overlapping.
|
||||
//!
|
||||
//! @param[in] __a First mdspan
|
||||
//! @param[in] __b Second mdspan
|
||||
//! @return true if the byte ranges of the two mdspans overlap
|
||||
template <typename _Tp1,
|
||||
typename _Extents1,
|
||||
typename _LayoutPolicy1,
|
||||
typename _AccessorPolicy1,
|
||||
typename _Tp2,
|
||||
typename _Extents2,
|
||||
typename _LayoutPolicy2,
|
||||
typename _AccessorPolicy2>
|
||||
[[nodiscard]] _CCCL_HOST_API bool
|
||||
__may_overlap(const ::cuda::std::mdspan<_Tp1, _Extents1, _LayoutPolicy1, _AccessorPolicy1>& __a,
|
||||
const ::cuda::std::mdspan<_Tp2, _Extents2, _LayoutPolicy2, _AccessorPolicy2>& __b) noexcept
|
||||
{
|
||||
if (__a.size() == 0 || __b.size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto* __a_begin = reinterpret_cast<const char*>(__a.data_handle());
|
||||
const auto* __b_begin = reinterpret_cast<const char*>(__b.data_handle());
|
||||
const auto* __a_end = __a_begin + __a.mapping().required_span_size() * sizeof(_Tp1);
|
||||
const auto* __b_end = __b_begin + __b.mapping().required_span_size() * sizeof(_Tp2);
|
||||
return ::cuda::ranges_overlap(__a_begin, __a_end, __b_begin, __b_end);
|
||||
}
|
||||
} // namespace cuda::experimental
|
||||
|
||||
# include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
#endif // _CUDAX__COPY_TENSOR_COPY_UTILS_H
|
||||
@@ -1,170 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_TENSOR_ITERATOR_H
|
||||
#define _CUDAX__COPY_TENSOR_ITERATOR_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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 <cuda/__cmath/fast_modulo_division.h>
|
||||
#include <cuda/std/__cstddef/types.h>
|
||||
#include <cuda/std/__type_traits/make_unsigned.h>
|
||||
#include <cuda/std/__type_traits/remove_const.h>
|
||||
#include <cuda/std/__utility/integer_sequence.h>
|
||||
#include <cuda/std/array>
|
||||
|
||||
#include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
/***********************************************************************************************************************
|
||||
* Fast Modulo/Division based on Precomputation
|
||||
**********************************************************************************************************************/
|
||||
|
||||
template <typename _ExtentT, ::cuda::std::size_t _Size, ::cuda::std::size_t... _Rp>
|
||||
[[nodiscard]] _CCCL_HOST_DEVICE_API ::cuda::std::array<::cuda::fast_mod_div<_ExtentT>, sizeof...(_Rp)>
|
||||
__extents_fast_div_mod_impl(const ::cuda::std::array<_ExtentT, _Size>& __extents,
|
||||
::cuda::std::index_sequence<_Rp...> = {}) noexcept
|
||||
{
|
||||
using __fast_mod_div_t = ::cuda::fast_mod_div<_ExtentT>;
|
||||
using __array_t = ::cuda::std::array<__fast_mod_div_t, sizeof...(_Rp)>;
|
||||
return __array_t{__fast_mod_div_t(__extents[_Rp])...};
|
||||
}
|
||||
|
||||
//! @brief Precompute modulo/division for each array extent.
|
||||
//!
|
||||
//! @param[in] __extents Array of extents
|
||||
//! @return Array of precomputed fast modulo/division objects
|
||||
template <typename _ExtentT, ::cuda::std::size_t _Size>
|
||||
[[nodiscard]] _CCCL_HOST_DEVICE_API ::cuda::std::array<::cuda::fast_mod_div<_ExtentT>, _Size>
|
||||
__extents_fast_div_mod(const ::cuda::std::array<_ExtentT, _Size>& __extents) noexcept
|
||||
{
|
||||
using __seq_t = ::cuda::std::make_index_sequence<_Size>;
|
||||
return ::cuda::experimental::__extents_fast_div_mod_impl(__extents, __seq_t{});
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* Tensor Coordinate Iterator and Partial Tensor
|
||||
**********************************************************************************************************************/
|
||||
|
||||
//! @brief Iterator that maps a linear tile index to a pointer into a strided raw tensor.
|
||||
template <typename _ExtentT, ::cuda::std::size_t _Rank>
|
||||
struct __tensor_coord_iterator
|
||||
{
|
||||
using __unsigned_extent_t = ::cuda::std::make_unsigned_t<_ExtentT>;
|
||||
using __fast_mod_div_t = ::cuda::fast_mod_div<__unsigned_extent_t>;
|
||||
using __array_t = ::cuda::std::array<__fast_mod_div_t, _Rank>;
|
||||
|
||||
__array_t __extents_;
|
||||
|
||||
//! @brief Convert an array of _UExtentT elements to an array of _ExtentT elements.
|
||||
//!
|
||||
//! @param[in] __in_array Source array with elements of type _UExtentT
|
||||
//! @return Array with elements statically cast to _ExtentT
|
||||
template <typename _UExtentT>
|
||||
[[nodiscard]] static _CCCL_HOST_API ::cuda::std::array<__unsigned_extent_t, _Rank>
|
||||
__to_extent_array(const ::cuda::std::array<_UExtentT, _Rank>& __in_array) noexcept
|
||||
{
|
||||
::cuda::std::array<__unsigned_extent_t, _Rank> __out_array{};
|
||||
for (::cuda::std::size_t __i = 0; __i < _Rank; ++__i)
|
||||
{
|
||||
__out_array[__i] = static_cast<__unsigned_extent_t>(__in_array[__i]);
|
||||
}
|
||||
return __out_array;
|
||||
}
|
||||
|
||||
//! @brief Constructs the iterator from tensor extents.
|
||||
//!
|
||||
//! @param[in] __extents Tensor extents (may be unsigned; converted to _ExtentT internally)
|
||||
template <typename _UExtentT>
|
||||
_CCCL_HOST_API explicit __tensor_coord_iterator(const ::cuda::std::array<_UExtentT, _Rank>& __extents) noexcept
|
||||
: __extents_{::cuda::experimental::__extents_fast_div_mod(__to_extent_array(__extents))}
|
||||
{}
|
||||
|
||||
//! @brief Returns the multi-dimensional coordinates for the given linear index.
|
||||
//!
|
||||
//! @param[in] __index Linear tile index
|
||||
//! @return Array of coordinates into the tensor
|
||||
[[nodiscard]] _CCCL_HOST_DEVICE_API ::cuda::std::array<_ExtentT, _Rank> operator()(_ExtentT __index) const noexcept
|
||||
{
|
||||
if constexpr (_Rank == 1)
|
||||
{
|
||||
return ::cuda::std::array<_ExtentT, _Rank>{{__index}};
|
||||
}
|
||||
else
|
||||
{
|
||||
// instead of computing the coordinate in parallel (index / prod(extent_i) % extent_i), we use a simpler and
|
||||
// slower approach. This saves registers and makes the overall computation faster.
|
||||
::cuda::std::array<_ExtentT, _Rank> __coords{};
|
||||
auto __quotient = static_cast<__unsigned_extent_t>(__index);
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __i = 0; __i < int{_Rank} - 1; ++__i)
|
||||
{
|
||||
const auto __div_result = ::cuda::div(__quotient, __extents_[__i]);
|
||||
__quotient = __div_result.first;
|
||||
__coords[__i] = static_cast<_ExtentT>(__div_result.second);
|
||||
}
|
||||
__coords[_Rank - 1] = static_cast<_ExtentT>(__quotient % __extents_[_Rank - 1]);
|
||||
return __coords;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//! @brief Lightweight device-side wrapper providing coordinate-indexed access to strided tensor data.
|
||||
//!
|
||||
//! Wraps a data pointer, per-dimension strides, and an accessor into a callable that maps
|
||||
//! multi-dimensional coordinates to element references.
|
||||
template <typename _Tp, typename _StrideT, ::cuda::std::size_t _Rank, typename _Accessor>
|
||||
struct __partial_tensor
|
||||
{
|
||||
_Tp* __ptr;
|
||||
::cuda::std::array<_StrideT, _Rank> __strides;
|
||||
_Accessor __accessor;
|
||||
|
||||
//! @brief Compute the linear offset for the given multi-dimensional coordinates.
|
||||
//!
|
||||
//! @param[in] __coords Array of per-dimension coordinates
|
||||
//! @return Linear offset into the tensor storage
|
||||
template <typename _CoordT>
|
||||
[[nodiscard]] _CCCL_DEVICE_API _StrideT __offset(const ::cuda::std::array<_CoordT, _Rank>& __coords) const noexcept
|
||||
{
|
||||
_StrideT __offset = 0;
|
||||
_CCCL_PRAGMA_UNROLL_FULL()
|
||||
for (int __i = 0; __i < int{_Rank}; ++__i)
|
||||
{
|
||||
__offset += static_cast<_StrideT>(__coords[__i]) * __strides[__i];
|
||||
}
|
||||
return __offset;
|
||||
}
|
||||
|
||||
//! @brief Access the element at the given multi-dimensional coordinates.
|
||||
//!
|
||||
//! @param[in] __coords Array of per-dimension coordinates
|
||||
//! @return Reference to the element at the computed offset
|
||||
template <typename _CoordT>
|
||||
[[nodiscard]] _CCCL_DEVICE_API decltype(auto)
|
||||
operator()(const ::cuda::std::array<_CoordT, _Rank>& __coords) const noexcept
|
||||
{
|
||||
return __accessor.access(const_cast<::cuda::std::remove_const_t<_Tp>*>(__ptr), __offset(__coords));
|
||||
}
|
||||
};
|
||||
} // namespace cuda::experimental
|
||||
|
||||
#include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // _CUDAX__COPY_TENSOR_ITERATOR_H
|
||||
@@ -1,74 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _CUDAX__COPY_VECTOR_ACCESS_H
|
||||
#define _CUDAX__COPY_VECTOR_ACCESS_H
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#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
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
# include <cuda/__driver/driver_api.h>
|
||||
# include <cuda/devices>
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
#include <cuda/std/__cstddef/types.h>
|
||||
|
||||
#include <cuda/std/__cccl/prologue.h>
|
||||
|
||||
namespace cuda::experimental
|
||||
{
|
||||
//! @brief Aligned storage type for vectorized memory access of a given byte width.
|
||||
template <::cuda::std::size_t _VectorBytes>
|
||||
struct alignas(_VectorBytes) __vector_access
|
||||
{
|
||||
char __data[_VectorBytes];
|
||||
};
|
||||
|
||||
// 32-byte accesses are supported since CTK 13.0
|
||||
#if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
inline constexpr auto __max_vector_access = 32;
|
||||
#else
|
||||
inline constexpr auto __max_vector_access = 16;
|
||||
#endif // _CCCL_CTK_AT_LEAST(13, 0)
|
||||
|
||||
#if !_CCCL_COMPILER(NVRTC)
|
||||
|
||||
template <::cuda::std::size_t _VectorBytes>
|
||||
using __vector_access_t = __vector_access<_VectorBytes>;
|
||||
|
||||
//! @brief Query the maximum vector access width supported by the current GPU architecture.
|
||||
//!
|
||||
//! @return Maximum vector width in bytes (32 for SM >= 10.0, 16 otherwise)
|
||||
[[nodiscard]] _CCCL_HOST_API inline ::cuda::std::size_t __max_gpu_arch_vector_size() noexcept
|
||||
{
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
const auto __dev_id = ::cuda::__driver::__cudevice_to_ordinal(::cuda::__driver::__ctxGetDevice());
|
||||
const auto __dev = ::cuda::devices[__dev_id];
|
||||
const auto __major = __dev.attribute<::cudaDevAttrComputeCapabilityMajor>();
|
||||
return (__major >= 10) ? 32 : 16;
|
||||
# else // ^^^ _CCCL_CTK_AT_LEAST(13, 0) ^^^ / vvv _CCCL_CTK_BELOW(13, 0) vvv
|
||||
return 16;
|
||||
# endif // _CCCL_CTK_BELOW(13, 0)
|
||||
}
|
||||
|
||||
#endif // !_CCCL_COMPILER(NVRTC)
|
||||
} // namespace cuda::experimental
|
||||
|
||||
#include <cuda/std/__cccl/epilogue.h>
|
||||
|
||||
#endif // _CUDAX__COPY_VECTOR_ACCESS_H
|
||||
Reference in New Issue
Block a user