mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-13 00:08:26 +08:00
Release v13.3 of the CUDA samples with CUDA 13.3 Toolkit (#435)
This is the release of the CUDA 13.3 samples, which include additions for CUDA Tile C++, and updated CCCL and Python samples.
This commit is contained in:
@@ -9,6 +9,9 @@ add_subdirectory(conjugateGradientMultiBlockCG)
|
||||
add_subdirectory(conjugateGradientMultiDeviceCG)
|
||||
add_subdirectory(conjugateGradientPrecond)
|
||||
add_subdirectory(conjugateGradientUM)
|
||||
add_subdirectory(cubDeviceFind)
|
||||
add_subdirectory(cubDeviceSegmentedScan)
|
||||
add_subdirectory(cubDeviceTransform)
|
||||
add_subdirectory(cudaNvSci)
|
||||
add_subdirectory(cuSolverDn_LinearSolver)
|
||||
add_subdirectory(cuSolverRf)
|
||||
@@ -18,6 +21,8 @@ add_subdirectory(cuSolverSp_LowlevelQR)
|
||||
add_subdirectory(freeImageInteropNPP)
|
||||
add_subdirectory(histEqualizationNPP)
|
||||
add_subdirectory(jitLto)
|
||||
add_subdirectory(libcuxxMdspan)
|
||||
add_subdirectory(libcuxxRandom)
|
||||
add_subdirectory(lineOfSight)
|
||||
add_subdirectory(matrixMulCUBLAS)
|
||||
add_subdirectory(nvJPEG)
|
||||
|
||||
62
cpp/4_CUDA_Libraries/cubDeviceFind/CMakeLists.txt
Normal file
62
cpp/4_CUDA_Libraries/cubDeviceFind/CMakeLists.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cubDeviceFind LANGUAGES C CXX CUDA)
|
||||
|
||||
# Disable response file for libraries on QNX as qcc does not support lib paths with double quotes
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Fetch CCCL via CPM.
|
||||
# Override with -DCCCL_SOURCE_DIR=/path/to/cccl to use a local checkout
|
||||
set(CCCL_SAMPLES_CCCL_TAG "v3.3.3" CACHE STRING
|
||||
"Tag/branch of NVIDIA/cccl to fetch for the CCCL samples")
|
||||
|
||||
if(NOT TARGET CCCL::CCCL)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CPM.cmake")
|
||||
if(DEFINED CCCL_SOURCE_DIR AND NOT CCCL_SOURCE_DIR STREQUAL "")
|
||||
CPMAddPackage(NAME CCCL SOURCE_DIR "${CCCL_SOURCE_DIR}")
|
||||
else()
|
||||
CPMAddPackage(
|
||||
NAME CCCL
|
||||
GIT_REPOSITORY "https://github.com/NVIDIA/cccl"
|
||||
GIT_TAG "${CCCL_SAMPLES_CCCL_TAG}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cubDeviceFind
|
||||
add_executable(cubDeviceFind cubDeviceFind.cu)
|
||||
|
||||
target_compile_options(cubDeviceFind PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cubDeviceFind PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cubDeviceFind PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(cubDeviceFind PRIVATE
|
||||
CUDA::cudart
|
||||
CCCL::CCCL
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
48
cpp/4_CUDA_Libraries/cubDeviceFind/README.md
Normal file
48
cpp/4_CUDA_Libraries/cubDeviceFind/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# cubDeviceFind - CUB DeviceFind Search Algorithms
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates the three device-wide search algorithms: `cub::DeviceFind::FindIf` for predicate search, and `cub::DeviceFind::LowerBound` / `UpperBound` for parallel binary search. Results are verified against `std::find_if`, `std::lower_bound`, and `std::upper_bound` on the host.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CCCL 3.3, CUB Device Algorithms, Parallel Search, Binary Search
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus) [SM 10.0 ](https://developer.nvidia.com/cuda-gpus) [SM 11.0 ](https://developer.nvidia.com/cuda-gpus) [SM 12.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CCCL CUB](https://nvidia.github.io/cccl/unstable/cub/index.html)
|
||||
|
||||
cub::DeviceFind::FindIf, cub::DeviceFind::LowerBound, cub::DeviceFind::UpperBound
|
||||
|
||||
### [CCCL libcu++](https://nvidia.github.io/cccl/unstable/libcudacxx/index.html)
|
||||
|
||||
cuda::std::less
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
|
||||
cudaDeviceSynchronize, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
|
||||
[CCCL 3.3+](https://github.com/NVIDIA/cccl). Fetched automatically via CPM at configure time (pinned to `v3.3.3`). Override with `-DCCCL_SOURCE_DIR=/path/to/cccl` to use a local checkout.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[CCCL 3.3 release notes](https://github.com/NVIDIA/cccl/releases), [cub::DeviceFind header](https://github.com/NVIDIA/cccl/blob/main/cub/cub/device/device_find.cuh)
|
||||
248
cpp/4_CUDA_Libraries/cubDeviceFind/cubDeviceFind.cu
Normal file
248
cpp/4_CUDA_Libraries/cubDeviceFind/cubDeviceFind.cu
Normal file
@@ -0,0 +1,248 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This sample demonstrates the three device-wide search algorithms
|
||||
* introduced in CCCL 3.3: cub::DeviceFind::FindIf for predicate search,
|
||||
* and cub::DeviceFind::LowerBound / UpperBound for parallel binary
|
||||
* search. Results are verified against std::find_if, std::lower_bound,
|
||||
* and std::upper_bound on the host.
|
||||
*/
|
||||
|
||||
/* Includes, system */
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
/* Includes, cuda */
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
/* Includes, cccl */
|
||||
#include <cub/device/device_find.cuh>
|
||||
#include <cuda/std/functional>
|
||||
#include <thrust/device_vector.h>
|
||||
#include <thrust/host_vector.h>
|
||||
|
||||
/* Predicate used with cub::DeviceFind::FindIf. */
|
||||
struct is_greater_than_t
|
||||
{
|
||||
int threshold;
|
||||
__host__ __device__ bool operator()(int value) const { return value > threshold; }
|
||||
};
|
||||
|
||||
static bool run_find_if()
|
||||
{
|
||||
/* Input: 0, 1, ..., 15. Predicate: value > 9. Expected index: 10. */
|
||||
const int num_items = 16;
|
||||
thrust::device_vector<int> d_in(num_items);
|
||||
for (int i = 0; i < num_items; ++i)
|
||||
d_in[i] = i;
|
||||
thrust::device_vector<int> d_out(1);
|
||||
is_greater_than_t predicate{9};
|
||||
|
||||
size_t temp_bytes = 0;
|
||||
checkCudaErrors(
|
||||
cub::DeviceFind::FindIf(nullptr, temp_bytes, d_in.begin(), d_out.begin(), predicate, num_items));
|
||||
thrust::device_vector<char> temp(temp_bytes);
|
||||
checkCudaErrors(cub::DeviceFind::FindIf(thrust::raw_pointer_cast(temp.data()),
|
||||
temp_bytes,
|
||||
d_in.begin(),
|
||||
d_out.begin(),
|
||||
predicate,
|
||||
num_items));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
const int got = d_out[0];
|
||||
|
||||
thrust::host_vector<int> h_in = d_in;
|
||||
auto host_it = std::find_if(h_in.begin(), h_in.end(),
|
||||
[&](int v) { return v > predicate.threshold; });
|
||||
const int expected = static_cast<int>(host_it - h_in.begin());
|
||||
|
||||
printf("cub::DeviceFind::FindIf(value > %d) over [0..%d)\n", predicate.threshold, num_items);
|
||||
printf(" got index = %d, expected = %d %s\n", got, expected, (got == expected ? "OK" : "FAIL"));
|
||||
return got == expected;
|
||||
}
|
||||
|
||||
static bool run_lower_bound()
|
||||
{
|
||||
/* Sorted range: [0, 2, 4, 6, 8]. Values to locate: [1, 3, 5, 7]. */
|
||||
thrust::device_vector<int> d_range = {0, 2, 4, 6, 8};
|
||||
thrust::device_vector<int> d_values = {1, 3, 5, 7};
|
||||
thrust::device_vector<int> d_out(d_values.size());
|
||||
|
||||
size_t temp_bytes = 0;
|
||||
checkCudaErrors(cub::DeviceFind::LowerBound(nullptr,
|
||||
temp_bytes,
|
||||
d_range.begin(),
|
||||
static_cast<int>(d_range.size()),
|
||||
d_values.begin(),
|
||||
static_cast<int>(d_values.size()),
|
||||
d_out.begin(),
|
||||
cuda::std::less{}));
|
||||
thrust::device_vector<char> temp(temp_bytes);
|
||||
checkCudaErrors(cub::DeviceFind::LowerBound(thrust::raw_pointer_cast(temp.data()),
|
||||
temp_bytes,
|
||||
d_range.begin(),
|
||||
static_cast<int>(d_range.size()),
|
||||
d_values.begin(),
|
||||
static_cast<int>(d_values.size()),
|
||||
d_out.begin(),
|
||||
cuda::std::less{}));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
thrust::host_vector<int> h_range = d_range;
|
||||
thrust::host_vector<int> h_values = d_values;
|
||||
thrust::host_vector<int> got = d_out;
|
||||
std::vector<int> expected(h_values.size());
|
||||
for (size_t i = 0; i < h_values.size(); ++i) {
|
||||
expected[i] = static_cast<int>(
|
||||
std::lower_bound(h_range.begin(), h_range.end(), h_values[i]) - h_range.begin());
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
printf("cub::DeviceFind::LowerBound\n");
|
||||
printf(" range = { 0, 2, 4, 6, 8 }\n");
|
||||
printf(" values = { 1, 3, 5, 7 }\n");
|
||||
printf(" got = {");
|
||||
for (size_t i = 0; i < got.size(); ++i) {
|
||||
printf(" %d", got[i]);
|
||||
if (got[i] != expected[i])
|
||||
ok = false;
|
||||
}
|
||||
printf(" }\n expect = {");
|
||||
for (size_t i = 0; i < expected.size(); ++i)
|
||||
printf(" %d", expected[i]);
|
||||
printf(" } %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool run_upper_bound()
|
||||
{
|
||||
/* Range with duplicates so LowerBound and UpperBound differ on values
|
||||
* that appear in the range. */
|
||||
thrust::device_vector<int> d_range = {0, 2, 2, 4, 6, 8};
|
||||
thrust::device_vector<int> d_values = {2, 2};
|
||||
thrust::device_vector<int> d_lb(d_values.size());
|
||||
thrust::device_vector<int> d_ub(d_values.size());
|
||||
|
||||
size_t temp_bytes_lb = 0;
|
||||
checkCudaErrors(cub::DeviceFind::LowerBound(nullptr,
|
||||
temp_bytes_lb,
|
||||
d_range.begin(),
|
||||
static_cast<int>(d_range.size()),
|
||||
d_values.begin(),
|
||||
static_cast<int>(d_values.size()),
|
||||
d_lb.begin(),
|
||||
cuda::std::less{}));
|
||||
thrust::device_vector<char> temp_lb(temp_bytes_lb);
|
||||
checkCudaErrors(cub::DeviceFind::LowerBound(thrust::raw_pointer_cast(temp_lb.data()),
|
||||
temp_bytes_lb,
|
||||
d_range.begin(),
|
||||
static_cast<int>(d_range.size()),
|
||||
d_values.begin(),
|
||||
static_cast<int>(d_values.size()),
|
||||
d_lb.begin(),
|
||||
cuda::std::less{}));
|
||||
|
||||
size_t temp_bytes_ub = 0;
|
||||
checkCudaErrors(cub::DeviceFind::UpperBound(nullptr,
|
||||
temp_bytes_ub,
|
||||
d_range.begin(),
|
||||
static_cast<int>(d_range.size()),
|
||||
d_values.begin(),
|
||||
static_cast<int>(d_values.size()),
|
||||
d_ub.begin(),
|
||||
cuda::std::less{}));
|
||||
thrust::device_vector<char> temp_ub(temp_bytes_ub);
|
||||
checkCudaErrors(cub::DeviceFind::UpperBound(thrust::raw_pointer_cast(temp_ub.data()),
|
||||
temp_bytes_ub,
|
||||
d_range.begin(),
|
||||
static_cast<int>(d_range.size()),
|
||||
d_values.begin(),
|
||||
static_cast<int>(d_values.size()),
|
||||
d_ub.begin(),
|
||||
cuda::std::less{}));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
thrust::host_vector<int> h_range = d_range;
|
||||
thrust::host_vector<int> h_values = d_values;
|
||||
thrust::host_vector<int> got_lb = d_lb;
|
||||
thrust::host_vector<int> got_ub = d_ub;
|
||||
std::vector<int> exp_lb(h_values.size());
|
||||
std::vector<int> exp_ub(h_values.size());
|
||||
for (size_t i = 0; i < h_values.size(); ++i) {
|
||||
exp_lb[i] =
|
||||
static_cast<int>(std::lower_bound(h_range.begin(), h_range.end(), h_values[i]) - h_range.begin());
|
||||
exp_ub[i] =
|
||||
static_cast<int>(std::upper_bound(h_range.begin(), h_range.end(), h_values[i]) - h_range.begin());
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
printf("cub::DeviceFind::UpperBound (with duplicates in range)\n");
|
||||
printf(" range = { 0, 2, 2, 4, 6, 8 }\n");
|
||||
printf(" values = { 2, 2 }\n");
|
||||
printf(" lb = {");
|
||||
for (size_t i = 0; i < got_lb.size(); ++i) {
|
||||
printf(" %d", got_lb[i]);
|
||||
if (got_lb[i] != exp_lb[i])
|
||||
ok = false;
|
||||
}
|
||||
printf(" } expected = {");
|
||||
for (size_t i = 0; i < exp_lb.size(); ++i)
|
||||
printf(" %d", exp_lb[i]);
|
||||
printf(" }\n ub = {");
|
||||
for (size_t i = 0; i < got_ub.size(); ++i) {
|
||||
printf(" %d", got_ub[i]);
|
||||
if (got_ub[i] != exp_ub[i])
|
||||
ok = false;
|
||||
}
|
||||
printf(" } expected = {");
|
||||
for (size_t i = 0; i < exp_ub.size(); ++i)
|
||||
printf(" %d", exp_ub[i]);
|
||||
printf(" } %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp props;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&props, devID));
|
||||
printf("Device: %s (Compute Capability %d.%d)\n\n", props.name, props.major, props.minor);
|
||||
|
||||
bool ok = true;
|
||||
ok &= run_find_if();
|
||||
printf("\n");
|
||||
ok &= run_lower_bound();
|
||||
printf("\n");
|
||||
ok &= run_upper_bound();
|
||||
|
||||
printf("\n%s\n", ok ? "Done" : "FAILED");
|
||||
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
63
cpp/4_CUDA_Libraries/cubDeviceSegmentedScan/CMakeLists.txt
Normal file
63
cpp/4_CUDA_Libraries/cubDeviceSegmentedScan/CMakeLists.txt
Normal file
@@ -0,0 +1,63 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cubDeviceSegmentedScan LANGUAGES C CXX CUDA)
|
||||
|
||||
# Disable response file for libraries on QNX as qcc does not support lib paths with double quotes
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Fetch CCCL via CPM.
|
||||
# Override with -DCCCL_SOURCE_DIR=/path/to/cccl to use a local checkout
|
||||
# instead of fetching from GitHub.
|
||||
set(CCCL_SAMPLES_CCCL_TAG "v3.3.3" CACHE STRING
|
||||
"Tag/branch of NVIDIA/cccl to fetch for the CCCL samples")
|
||||
|
||||
if(NOT TARGET CCCL::CCCL)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CPM.cmake")
|
||||
if(DEFINED CCCL_SOURCE_DIR AND NOT CCCL_SOURCE_DIR STREQUAL "")
|
||||
CPMAddPackage(NAME CCCL SOURCE_DIR "${CCCL_SOURCE_DIR}")
|
||||
else()
|
||||
CPMAddPackage(
|
||||
NAME CCCL
|
||||
GIT_REPOSITORY "https://github.com/NVIDIA/cccl"
|
||||
GIT_TAG "${CCCL_SAMPLES_CCCL_TAG}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cubDeviceSegmentedScan
|
||||
add_executable(cubDeviceSegmentedScan cubDeviceSegmentedScan.cu)
|
||||
|
||||
target_compile_options(cubDeviceSegmentedScan PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cubDeviceSegmentedScan PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cubDeviceSegmentedScan PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(cubDeviceSegmentedScan PRIVATE
|
||||
CUDA::cudart
|
||||
CCCL::CCCL
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
48
cpp/4_CUDA_Libraries/cubDeviceSegmentedScan/README.md
Normal file
48
cpp/4_CUDA_Libraries/cubDeviceSegmentedScan/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# cubDeviceSegmentedScan - CUB DeviceSegmentedScan
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates `cub::DeviceSegmentedScan`. A segmented scan computes an independent scan over each of many contiguous segments in a single device-wide call. Two operations are shown: `ExclusiveSegmentedSum` across three independent segments, and `InclusiveSegmentedScan` with a custom binary operator (running maximum via `cuda::maximum<>`).
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CUB Device Algorithms, Segmented Scan, Prefix Sum
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus) [SM 10.0 ](https://developer.nvidia.com/cuda-gpus) [SM 11.0 ](https://developer.nvidia.com/cuda-gpus) [SM 12.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CCCL CUB](https://nvidia.github.io/cccl/cub/)
|
||||
|
||||
cub::DeviceSegmentedScan::ExclusiveSegmentedSum, cub::DeviceSegmentedScan::InclusiveSegmentedScan
|
||||
|
||||
### [CCCL libcu++](https://nvidia.github.io/cccl/libcudacxx/)
|
||||
|
||||
cuda::maximum
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
|
||||
cudaDeviceSynchronize, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
|
||||
[CCCL 3.3+](https://github.com/NVIDIA/cccl). Fetched automatically via CPM at configure time (pinned to `v3.3.3`). Override with `-DCCCL_SOURCE_DIR=/path/to/cccl` to use a local checkout.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[CCCL 3.3 release notes](https://github.com/NVIDIA/cccl/releases), [cub::DeviceSegmentedScan header](https://github.com/NVIDIA/cccl/blob/main/cub/cub/device/device_segmented_scan.cuh)
|
||||
@@ -0,0 +1,188 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This sample demonstrates cub::DeviceSegmentedScan, added in CCCL 3.3.
|
||||
* Two operations are shown: ExclusiveSegmentedSum across three independent
|
||||
* segments, and InclusiveSegmentedScan with a custom binary operator
|
||||
* (running maximum via cuda::maximum<>). Each is verified against a
|
||||
* host reference implementation.
|
||||
*/
|
||||
|
||||
/* Includes, system */
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
/* Includes, cuda */
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
/* Includes, cccl */
|
||||
#include <cub/device/device_segmented_scan.cuh>
|
||||
#include <cuda/functional>
|
||||
#include <thrust/device_vector.h>
|
||||
#include <thrust/host_vector.h>
|
||||
|
||||
template <typename T>
|
||||
static void print_vec(const char *label, const std::vector<T> &v)
|
||||
{
|
||||
printf(" %-24s{", label);
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
printf(" %d", static_cast<int>(v[i]));
|
||||
printf(" }\n");
|
||||
}
|
||||
|
||||
static std::vector<int> host_exclusive_segmented_sum(const std::vector<int> &input, const std::vector<size_t> &offsets)
|
||||
{
|
||||
std::vector<int> out(input.size(), 0);
|
||||
for (size_t s = 0; s + 1 < offsets.size(); ++s) {
|
||||
int running = 0;
|
||||
for (size_t i = offsets[s]; i < offsets[s + 1]; ++i) {
|
||||
out[i] = running;
|
||||
running += input[i];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<int> host_inclusive_segmented_max(const std::vector<int> &input, const std::vector<size_t> &offsets)
|
||||
{
|
||||
std::vector<int> out(input.size(), 0);
|
||||
for (size_t s = 0; s + 1 < offsets.size(); ++s) {
|
||||
int running = std::numeric_limits<int>::min();
|
||||
for (size_t i = offsets[s]; i < offsets[s + 1]; ++i) {
|
||||
running = std::max(running, input[i]);
|
||||
out[i] = running;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool run_exclusive_segmented_sum()
|
||||
{
|
||||
/* 3 segments: [1,2,3] [4,5] [6,7,8] -> [0,1,3] [0,4] [0,6,13]. */
|
||||
thrust::device_vector<int> d_in = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
thrust::device_vector<size_t> d_offsets = {0, 3, 5, 8};
|
||||
thrust::device_vector<int> d_out(d_in.size());
|
||||
|
||||
const auto num_segments = d_offsets.size() - 1;
|
||||
auto begin_offsets = d_offsets.begin();
|
||||
auto end_offsets = d_offsets.begin() + 1;
|
||||
|
||||
size_t temp_bytes = 0;
|
||||
checkCudaErrors(cub::DeviceSegmentedScan::ExclusiveSegmentedSum(
|
||||
nullptr, temp_bytes, d_in.begin(), d_out.begin(), begin_offsets, end_offsets, num_segments));
|
||||
thrust::device_vector<char> temp(temp_bytes);
|
||||
checkCudaErrors(cub::DeviceSegmentedScan::ExclusiveSegmentedSum(thrust::raw_pointer_cast(temp.data()),
|
||||
temp_bytes,
|
||||
d_in.begin(),
|
||||
d_out.begin(),
|
||||
begin_offsets,
|
||||
end_offsets,
|
||||
num_segments));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
std::vector<int> h_in(d_in.begin(), d_in.end());
|
||||
std::vector<size_t> h_off(d_offsets.begin(), d_offsets.end());
|
||||
std::vector<int> got(d_out.begin(), d_out.end());
|
||||
std::vector<int> expected = host_exclusive_segmented_sum(h_in, h_off);
|
||||
|
||||
printf("cub::DeviceSegmentedScan::ExclusiveSegmentedSum\n");
|
||||
print_vec("input:", h_in);
|
||||
printf(" %-24s{", "offsets:");
|
||||
for (auto o : h_off)
|
||||
printf(" %zu", o);
|
||||
printf(" }\n");
|
||||
print_vec("got:", got);
|
||||
print_vec("expected:", expected);
|
||||
const bool ok = got == expected;
|
||||
printf(" %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool run_inclusive_segmented_max()
|
||||
{
|
||||
/* Same three segments, but compute running max per segment. */
|
||||
thrust::device_vector<int> d_in = {3, 1, 4, 5, 2, 9, 7, 8};
|
||||
thrust::device_vector<size_t> d_offsets = {0, 3, 5, 8};
|
||||
thrust::device_vector<int> d_out(d_in.size());
|
||||
|
||||
const auto num_segments = d_offsets.size() - 1;
|
||||
auto begin_offsets = d_offsets.begin();
|
||||
auto end_offsets = d_offsets.begin() + 1;
|
||||
|
||||
auto max_op = [] __host__ __device__(int a, int b) -> int { return cuda::maximum<>{}(a, b); };
|
||||
|
||||
size_t temp_bytes = 0;
|
||||
checkCudaErrors(cub::DeviceSegmentedScan::InclusiveSegmentedScan(
|
||||
nullptr, temp_bytes, d_in.begin(), d_out.begin(), begin_offsets, end_offsets, num_segments, max_op));
|
||||
thrust::device_vector<char> temp(temp_bytes);
|
||||
checkCudaErrors(cub::DeviceSegmentedScan::InclusiveSegmentedScan(thrust::raw_pointer_cast(temp.data()),
|
||||
temp_bytes,
|
||||
d_in.begin(),
|
||||
d_out.begin(),
|
||||
begin_offsets,
|
||||
end_offsets,
|
||||
num_segments,
|
||||
max_op));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
std::vector<int> h_in(d_in.begin(), d_in.end());
|
||||
std::vector<size_t> h_off(d_offsets.begin(), d_offsets.end());
|
||||
std::vector<int> got(d_out.begin(), d_out.end());
|
||||
std::vector<int> expected = host_inclusive_segmented_max(h_in, h_off);
|
||||
|
||||
printf("cub::DeviceSegmentedScan::InclusiveSegmentedScan (running max)\n");
|
||||
print_vec("input:", h_in);
|
||||
printf(" %-24s{", "offsets:");
|
||||
for (auto o : h_off)
|
||||
printf(" %zu", o);
|
||||
printf(" }\n");
|
||||
print_vec("got:", got);
|
||||
print_vec("expected:", expected);
|
||||
const bool ok = got == expected;
|
||||
printf(" %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp props;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&props, devID));
|
||||
printf("Device: %s (Compute Capability %d.%d)\n\n", props.name, props.major, props.minor);
|
||||
|
||||
bool ok = true;
|
||||
ok &= run_exclusive_segmented_sum();
|
||||
printf("\n");
|
||||
ok &= run_inclusive_segmented_max();
|
||||
|
||||
printf("\n%s\n", ok ? "Done" : "FAILED");
|
||||
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
62
cpp/4_CUDA_Libraries/cubDeviceTransform/CMakeLists.txt
Normal file
62
cpp/4_CUDA_Libraries/cubDeviceTransform/CMakeLists.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cubDeviceTransform LANGUAGES C CXX CUDA)
|
||||
|
||||
# Disable response file for libraries on QNX as qcc does not support lib paths with double quotes
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Fetch CCCL via CPM.
|
||||
# Override with -DCCCL_SOURCE_DIR=/path/to/cccl to use a local checkout
|
||||
set(CCCL_SAMPLES_CCCL_TAG "v3.3.3" CACHE STRING
|
||||
"Tag/branch of NVIDIA/cccl to fetch for the CCCL samples")
|
||||
|
||||
if(NOT TARGET CCCL::CCCL)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CPM.cmake")
|
||||
if(DEFINED CCCL_SOURCE_DIR AND NOT CCCL_SOURCE_DIR STREQUAL "")
|
||||
CPMAddPackage(NAME CCCL SOURCE_DIR "${CCCL_SOURCE_DIR}")
|
||||
else()
|
||||
CPMAddPackage(
|
||||
NAME CCCL
|
||||
GIT_REPOSITORY "https://github.com/NVIDIA/cccl"
|
||||
GIT_TAG "${CCCL_SAMPLES_CCCL_TAG}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cubDeviceTransform
|
||||
add_executable(cubDeviceTransform cubDeviceTransform.cu)
|
||||
|
||||
target_compile_options(cubDeviceTransform PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cubDeviceTransform PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cubDeviceTransform PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(cubDeviceTransform PRIVATE
|
||||
CUDA::cudart
|
||||
CCCL::CCCL
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
48
cpp/4_CUDA_Libraries/cubDeviceTransform/README.md
Normal file
48
cpp/4_CUDA_Libraries/cubDeviceTransform/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# cubDeviceTransform - CUB DeviceTransform N-to-M
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates `cub::DeviceTransform` in its N-input / M-output form. A single device-wide call reads from N input sequences and writes to M output sequences, driven by a user-provided op that returns a `cuda::std::tuple` of M values. Two cases are shown: N=3 inputs producing 1 output, and N=2 inputs producing 2 outputs (sum and difference in one fused pass).
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CCCL 3.3, CUB Device Algorithms, Fused Elementwise Transforms, Counting Iterators
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus) [SM 10.0 ](https://developer.nvidia.com/cuda-gpus) [SM 11.0 ](https://developer.nvidia.com/cuda-gpus) [SM 12.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CCCL CUB](https://nvidia.github.io/cccl/cub/)
|
||||
|
||||
cub::DeviceTransform::Transform
|
||||
|
||||
### [CCCL libcu++](https://nvidia.github.io/cccl/libcudacxx/)
|
||||
|
||||
cuda::counting_iterator, cuda::std::tuple
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
|
||||
cudaDeviceSynchronize, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
|
||||
[CCCL 3.3+](https://github.com/NVIDIA/cccl). Fetched automatically via CPM at configure time (pinned to `v3.3.3`). Override with `-DCCCL_SOURCE_DIR=/path/to/cccl` to use a local checkout.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[CCCL 3.3 release notes](https://github.com/NVIDIA/cccl/releases), [cub::DeviceTransform header](https://github.com/NVIDIA/cccl/blob/main/cub/cub/device/device_transform.cuh)
|
||||
155
cpp/4_CUDA_Libraries/cubDeviceTransform/cubDeviceTransform.cu
Normal file
155
cpp/4_CUDA_Libraries/cubDeviceTransform/cubDeviceTransform.cu
Normal file
@@ -0,0 +1,155 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This sample demonstrates cub::DeviceTransform in its N-input/M-output
|
||||
* form (extended in CCCL 3.3). A single device-wide call reads from
|
||||
* N input sequences and writes to M output sequences, driven by a
|
||||
* user-provided op that returns a tuple of M values. Two cases are
|
||||
* shown: N=3 -> 1 and N=2 -> 2. Results are verified against a host
|
||||
* reference.
|
||||
*/
|
||||
|
||||
/* Includes, system */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
/* Includes, cuda */
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
/* Includes, cccl */
|
||||
#include <cub/device/device_transform.cuh>
|
||||
#include <cuda/iterator>
|
||||
#include <thrust/device_vector.h>
|
||||
#include <thrust/host_vector.h>
|
||||
|
||||
static bool run_n_to_one_transform()
|
||||
{
|
||||
/* result[i] = (a[i] + b[i]) * c[i], with c = counting_iterator<int>(100). */
|
||||
thrust::device_vector<int> a = {0, -2, 5, 3};
|
||||
thrust::device_vector<float> b = {5.2f, 3.1f, -1.1f, 3.0f};
|
||||
auto counting = cuda::counting_iterator<int>{100};
|
||||
thrust::device_vector<int> result(a.size());
|
||||
|
||||
auto op = [] __host__ __device__(int x, float y, int z) -> int {
|
||||
return static_cast<int>((x + y) * z);
|
||||
};
|
||||
|
||||
checkCudaErrors(cub::DeviceTransform::Transform(
|
||||
cuda::std::tuple{a.begin(), b.begin(), counting}, result.begin(), a.size(), op));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
thrust::host_vector<int> ha = a;
|
||||
thrust::host_vector<float> hb = b;
|
||||
thrust::host_vector<int> got = result;
|
||||
std::vector<int> expected(a.size());
|
||||
for (size_t i = 0; i < a.size(); ++i) {
|
||||
expected[i] = static_cast<int>((ha[i] + hb[i]) * static_cast<int>(100 + i));
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
printf("cub::DeviceTransform::Transform (N=3 inputs -> 1 output)\n");
|
||||
printf(" result = (a + b) * c with c = counting_iterator(100)\n");
|
||||
printf(" got = {");
|
||||
for (size_t i = 0; i < got.size(); ++i) {
|
||||
printf(" %d", got[i]);
|
||||
if (got[i] != expected[i])
|
||||
ok = false;
|
||||
}
|
||||
printf(" }\n expected = {");
|
||||
for (size_t i = 0; i < expected.size(); ++i)
|
||||
printf(" %d", expected[i]);
|
||||
printf(" } %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool run_n_to_m_transform()
|
||||
{
|
||||
/* (sum[i], diff[i]) = (a[i] + b[i], a[i] - b[i]) in one pass. */
|
||||
thrust::device_vector<int> a = {1, 5, 10, 7, 3};
|
||||
thrust::device_vector<int> b = {4, 2, 8, 1, 9};
|
||||
thrust::device_vector<int> sum(a.size());
|
||||
thrust::device_vector<int> diff(a.size());
|
||||
|
||||
auto op = [] __host__ __device__(int x, int y) -> cuda::std::tuple<int, int> {
|
||||
return {x + y, x - y};
|
||||
};
|
||||
|
||||
checkCudaErrors(cub::DeviceTransform::Transform(cuda::std::tuple{a.begin(), b.begin()},
|
||||
cuda::std::tuple{sum.begin(), diff.begin()},
|
||||
a.size(),
|
||||
op));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
thrust::host_vector<int> ha = a, hb = b, got_sum = sum, got_diff = diff;
|
||||
std::vector<int> exp_sum(a.size()), exp_diff(a.size());
|
||||
for (size_t i = 0; i < a.size(); ++i) {
|
||||
exp_sum[i] = ha[i] + hb[i];
|
||||
exp_diff[i] = ha[i] - hb[i];
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
printf("cub::DeviceTransform::Transform (N=2 inputs -> M=2 outputs)\n");
|
||||
printf(" op returns cuda::std::tuple{a + b, a - b}\n");
|
||||
printf(" sum = {");
|
||||
for (size_t i = 0; i < got_sum.size(); ++i) {
|
||||
printf(" %d", got_sum[i]);
|
||||
if (got_sum[i] != exp_sum[i])
|
||||
ok = false;
|
||||
}
|
||||
printf(" } expected = {");
|
||||
for (auto v : exp_sum)
|
||||
printf(" %d", v);
|
||||
printf(" }\n diff = {");
|
||||
for (size_t i = 0; i < got_diff.size(); ++i) {
|
||||
printf(" %d", got_diff[i]);
|
||||
if (got_diff[i] != exp_diff[i])
|
||||
ok = false;
|
||||
}
|
||||
printf(" } expected = {");
|
||||
for (auto v : exp_diff)
|
||||
printf(" %d", v);
|
||||
printf(" } %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp props;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&props, devID));
|
||||
printf("Device: %s (Compute Capability %d.%d)\n\n", props.name, props.major, props.minor);
|
||||
|
||||
bool ok = true;
|
||||
ok &= run_n_to_one_transform();
|
||||
printf("\n");
|
||||
ok &= run_n_to_m_transform();
|
||||
|
||||
printf("\n%s\n", ok ? "Done" : "FAILED");
|
||||
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
92
cpp/4_CUDA_Libraries/libcuxxMdspan/CMakeLists.txt
Normal file
92
cpp/4_CUDA_Libraries/libcuxxMdspan/CMakeLists.txt
Normal file
@@ -0,0 +1,92 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(libcuxxMdspan LANGUAGES C CXX CUDA)
|
||||
|
||||
# Disable response file for libraries on QNX as qcc does not support lib paths with double quotes
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Fetch CCCL (CUB + libcu++ + Thrust) via CPM. The toolkit that ships
|
||||
# with CUDA 13.2 bundles CCCL 3.2, but this sample uses APIs added in
|
||||
# CCCL 3.3. Pinning the tag here lets the sample build on any toolkit
|
||||
# with a usable nvcc. Override with -DCCCL_SOURCE_DIR=/path/to/cccl
|
||||
# to use a local checkout instead of fetching from GitHub.
|
||||
set(CCCL_SAMPLES_CCCL_TAG "v3.3.3" CACHE STRING
|
||||
"Tag/branch of NVIDIA/cccl to fetch for the CCCL samples")
|
||||
|
||||
if(NOT TARGET CCCL::CCCL)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CPM.cmake")
|
||||
if(DEFINED CCCL_SOURCE_DIR AND NOT CCCL_SOURCE_DIR STREQUAL "")
|
||||
CPMAddPackage(NAME CCCL SOURCE_DIR "${CCCL_SOURCE_DIR}")
|
||||
else()
|
||||
CPMAddPackage(
|
||||
NAME CCCL
|
||||
GIT_REPOSITORY "https://github.com/NVIDIA/cccl"
|
||||
GIT_TAG "${CCCL_SAMPLES_CCCL_TAG}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# DLPack headers are required for cuda::to_device_mdspan and
|
||||
# cuda::to_dlpack_tensor. Fetch via CPM (override with DLPACK_SOURCE_DIR).
|
||||
set(CCCL_SAMPLES_DLPACK_TAG "v1.3" CACHE STRING
|
||||
"Tag of dmlc/dlpack to fetch for libcuxxMdspan")
|
||||
|
||||
if(NOT TARGET dlpack::dlpack)
|
||||
if(NOT COMMAND CPMAddPackage)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CPM.cmake")
|
||||
endif()
|
||||
if(DEFINED DLPACK_SOURCE_DIR AND NOT DLPACK_SOURCE_DIR STREQUAL "")
|
||||
CPMAddPackage(NAME dlpack SOURCE_DIR "${DLPACK_SOURCE_DIR}" DOWNLOAD_ONLY YES)
|
||||
else()
|
||||
CPMAddPackage(
|
||||
NAME dlpack
|
||||
GIT_REPOSITORY "https://github.com/dmlc/dlpack"
|
||||
GIT_TAG "${CCCL_SAMPLES_DLPACK_TAG}"
|
||||
DOWNLOAD_ONLY YES
|
||||
)
|
||||
endif()
|
||||
if(dlpack_ADDED)
|
||||
add_library(dlpack_headers INTERFACE)
|
||||
target_include_directories(dlpack_headers INTERFACE "${dlpack_SOURCE_DIR}/include")
|
||||
add_library(dlpack::dlpack ALIAS dlpack_headers)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for libcuxxMdspan
|
||||
add_executable(libcuxxMdspan libcuxxMdspan.cu)
|
||||
|
||||
target_compile_options(libcuxxMdspan PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(libcuxxMdspan PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(libcuxxMdspan PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(libcuxxMdspan PRIVATE
|
||||
CUDA::cudart
|
||||
CCCL::CCCL
|
||||
dlpack::dlpack
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
44
cpp/4_CUDA_Libraries/libcuxxMdspan/README.md
Normal file
44
cpp/4_CUDA_Libraries/libcuxxMdspan/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# libcuxxMdspan - libcu++ mdspan Interop (DLPack + shared_memory_mdspan)
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates two mdspan-centric features CCCL: DLPack <-> `cuda::std::mdspan` bridging via `cuda::to_device_mdspan` / `cuda::to_dlpack_tensor` (the tensor-interchange protocol used by PyTorch, JAX, CuPy, and others), and `cuda::shared_memory_mdspan` for multi-dimensional views of shared-memory tiles with address-space-safe accessors. A small matrix is built, wrapped in a DLTensor, converted to a `device_mdspan`, scaled row-wise, and transposed through a `shared_memory_mdspan` tile. The output mdspan is converted back to DLPack and its metadata is printed.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CCCL 3.3, libcu++ mdspan, DLPack Interoperability, Shared Memory Views
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus) [SM 10.0 ](https://developer.nvidia.com/cuda-gpus) [SM 11.0 ](https://developer.nvidia.com/cuda-gpus) [SM 12.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CCCL libcu++](https://nvidia.github.io/cccl/libcudacxx/)
|
||||
|
||||
cuda::to_device_mdspan, cuda::to_dlpack_tensor, cuda::device_mdspan, cuda::shared_memory_mdspan, cuda::std::mdspan
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
|
||||
cudaMalloc, cudaFree, cudaMemcpy, cudaMemset, cudaDeviceSynchronize, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
|
||||
[CCCL 3.3+](https://github.com/NVIDIA/cccl), [DLPack 1.2+](https://github.com/dmlc/dlpack). Both fetched automatically via CPM at configure time (pinned to `v3.3.3` and `v1.3` respectively). Override with `-DCCCL_SOURCE_DIR=/path/to/cccl` and `-DDLPACK_SOURCE_DIR=/path/to/dlpack` to use local checkouts.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[CCCL 3.3 release notes](https://github.com/NVIDIA/cccl/releases), [cuda::to_device_mdspan header](https://github.com/NVIDIA/cccl/blob/main/libcudacxx/include/cuda/__mdspan/dlpack_to_mdspan.h), [cuda::shared_memory_mdspan docs](https://nvidia.github.io/cccl/libcudacxx/extended_api/mdspan/shared_memory_accessor.html), [DLPack specification](https://dmlc.github.io/dlpack/latest/)
|
||||
246
cpp/4_CUDA_Libraries/libcuxxMdspan/libcuxxMdspan.cu
Normal file
246
cpp/4_CUDA_Libraries/libcuxxMdspan/libcuxxMdspan.cu
Normal file
@@ -0,0 +1,246 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This sample demonstrates two mdspan-centric features from CCCL 3.3:
|
||||
*
|
||||
* 1. DLPack <-> cuda::std::mdspan bridging through
|
||||
* cuda::to_device_mdspan<T, Rank>(DLTensor) -> cuda::device_mdspan
|
||||
* cuda::to_dlpack_tensor(device_mdspan) -> DLManagedTensor
|
||||
* The DLPack format is the interchange protocol used by PyTorch,
|
||||
* JAX, CuPy, and other frameworks; cuda::device_mdspan is the
|
||||
* device-side view with rich shape/stride metadata for kernels.
|
||||
*
|
||||
* 2. cuda::shared_memory_mdspan: a multi-dimensional view over a
|
||||
* shared-memory tile. The accessor guarantees shared-memory
|
||||
* load/store instructions and adds address-space safety checks.
|
||||
*
|
||||
* A sample matrix is built on the device, wrapped in a DLTensor,
|
||||
* converted to a cuda::device_mdspan, and two kernels run against it:
|
||||
* scale_rows_kernel multiplies row i by (i + 1), and
|
||||
* shared_tile_transpose_kernel uses a cuda::shared_memory_mdspan to
|
||||
* transpose a block-sized tile through shared memory. The output
|
||||
* mdspan is then converted back to DLPack metadata and printed.
|
||||
*/
|
||||
|
||||
/* Includes, system */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
/* Includes, cuda */
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
/* Includes, cccl */
|
||||
#include <cuda/mdspan>
|
||||
#include <cuda/std/array>
|
||||
#include <cuda/std/cstdint>
|
||||
#include <cuda/std/mdspan>
|
||||
|
||||
#define ROWS 8
|
||||
#define COLS 8
|
||||
#define TILE 8 /* matches ROWS / COLS for simplicity */
|
||||
|
||||
using extents2d = cuda::std::dextents<cuda::std::size_t, 2>;
|
||||
|
||||
/* Kernel A: multiply row i of a 2-D device_mdspan by (i + 1). Templated
|
||||
* on the mdspan type so it accepts the exact type produced by
|
||||
* cuda::to_device_mdspan (which uses layout_stride_relaxed and int64_t
|
||||
* extents). */
|
||||
template <typename Tensor>
|
||||
__global__ void scale_rows_kernel(Tensor tensor)
|
||||
{
|
||||
const int r = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
const int c = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (r < static_cast<int>(tensor.extent(0)) && c < static_cast<int>(tensor.extent(1))) {
|
||||
tensor(r, c) *= static_cast<float>(r + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Kernel B: block-tile transpose driven by a shared_memory_mdspan.
|
||||
* Each block loads a TILE x TILE tile from the input into shared memory
|
||||
* through a cuda::shared_memory_mdspan, transposes in shared, and writes
|
||||
* to the output. */
|
||||
template <typename InTensor, typename OutTensor>
|
||||
__global__ void shared_tile_transpose_kernel(InTensor in, OutTensor out)
|
||||
{
|
||||
__shared__ float smem_storage[TILE * TILE];
|
||||
cuda::shared_memory_mdspan smem(smem_storage, cuda::std::dextents<cuda::std::size_t, 2>{TILE, TILE});
|
||||
|
||||
const int tr = threadIdx.y;
|
||||
const int tc = threadIdx.x;
|
||||
const int r = blockIdx.y * TILE + tr;
|
||||
const int c = blockIdx.x * TILE + tc;
|
||||
|
||||
if (r < static_cast<int>(in.extent(0)) && c < static_cast<int>(in.extent(1))) {
|
||||
smem(tr, tc) = in(r, c);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const int r_out = blockIdx.x * TILE + tr;
|
||||
const int c_out = blockIdx.y * TILE + tc;
|
||||
if (r_out < static_cast<int>(out.extent(0)) && c_out < static_cast<int>(out.extent(1))) {
|
||||
out(r_out, c_out) = smem(tc, tr);
|
||||
}
|
||||
}
|
||||
|
||||
struct DLTensorStorage
|
||||
{
|
||||
::DLTensor tensor{};
|
||||
cuda::std::array<cuda::std::int64_t, 2> shape{};
|
||||
cuda::std::array<cuda::std::int64_t, 2> strides{};
|
||||
};
|
||||
|
||||
static DLTensorStorage make_row_major_dltensor(float *device_ptr, int rows, int cols, int device_ordinal)
|
||||
{
|
||||
DLTensorStorage s;
|
||||
s.shape = {rows, cols};
|
||||
s.strides = {cols, 1};
|
||||
s.tensor.data = device_ptr;
|
||||
s.tensor.device = ::DLDevice{::kDLCUDA, device_ordinal};
|
||||
s.tensor.ndim = 2;
|
||||
s.tensor.dtype = ::DLDataType{::DLDataTypeCode::kDLFloat, 32, 1};
|
||||
s.tensor.shape = s.shape.data();
|
||||
s.tensor.strides = s.strides.data();
|
||||
s.tensor.byte_offset = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp props;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&props, devID));
|
||||
printf("Device: %s (Compute Capability %d.%d)\n\n", props.name, props.major, props.minor);
|
||||
|
||||
float *d_in = nullptr;
|
||||
float *d_out = nullptr;
|
||||
const size_t nelem = static_cast<size_t>(ROWS) * COLS;
|
||||
checkCudaErrors(cudaMalloc(&d_in, nelem * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc(&d_out, nelem * sizeof(float)));
|
||||
|
||||
std::vector<float> host(nelem);
|
||||
for (int r = 0; r < ROWS; ++r) {
|
||||
for (int c = 0; c < COLS; ++c) {
|
||||
host[r * COLS + c] = static_cast<float>(r * COLS + c);
|
||||
}
|
||||
}
|
||||
checkCudaErrors(cudaMemcpy(d_in, host.data(), nelem * sizeof(float), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemset(d_out, 0, nelem * sizeof(float)));
|
||||
|
||||
DLTensorStorage in_dl = make_row_major_dltensor(d_in, ROWS, COLS, devID);
|
||||
DLTensorStorage out_dl = make_row_major_dltensor(d_out, ROWS, COLS, devID);
|
||||
|
||||
auto in_md = cuda::to_device_mdspan<float, 2>(in_dl.tensor);
|
||||
auto out_md = cuda::to_device_mdspan<float, 2>(out_dl.tensor);
|
||||
|
||||
printf("cuda::to_device_mdspan produced a 2-D device_mdspan of shape (%zu, %zu)\n\n",
|
||||
in_md.extent(0),
|
||||
in_md.extent(1));
|
||||
|
||||
dim3 block(8, 8);
|
||||
dim3 grid((COLS + block.x - 1) / block.x, (ROWS + block.y - 1) / block.y);
|
||||
scale_rows_kernel<<<grid, block>>>(in_md);
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
std::vector<float> scaled(nelem);
|
||||
checkCudaErrors(cudaMemcpy(scaled.data(), d_in, nelem * sizeof(float), cudaMemcpyDeviceToHost));
|
||||
bool scale_ok = true;
|
||||
for (int r = 0; r < ROWS && scale_ok; ++r) {
|
||||
for (int c = 0; c < COLS && scale_ok; ++c) {
|
||||
const float expect = static_cast<float>((r * COLS + c) * (r + 1));
|
||||
if (scaled[r * COLS + c] != expect) {
|
||||
printf("scale_rows mismatch at (%d,%d): got %g expected %g\n",
|
||||
r,
|
||||
c,
|
||||
scaled[r * COLS + c],
|
||||
expect);
|
||||
scale_ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scale_ok) {
|
||||
printf("scale_rows kernel: OK (row i scaled by i+1 via cuda::device_mdspan)\n");
|
||||
}
|
||||
|
||||
cuda::device_mdspan<const float, extents2d> in_md_const(d_in, extents2d{ROWS, COLS});
|
||||
cuda::device_mdspan<float, extents2d> out_md_rw(d_out, extents2d{ROWS, COLS});
|
||||
|
||||
dim3 tile_block(TILE, TILE);
|
||||
dim3 tile_grid((COLS + TILE - 1) / TILE, (ROWS + TILE - 1) / TILE);
|
||||
shared_tile_transpose_kernel<<<tile_grid, tile_block>>>(in_md_const, out_md_rw);
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
std::vector<float> transposed(nelem);
|
||||
checkCudaErrors(cudaMemcpy(transposed.data(), d_out, nelem * sizeof(float), cudaMemcpyDeviceToHost));
|
||||
bool tp_ok = true;
|
||||
for (int r = 0; r < ROWS && tp_ok; ++r) {
|
||||
for (int c = 0; c < COLS && tp_ok; ++c) {
|
||||
const float expect = scaled[c * COLS + r];
|
||||
if (transposed[r * COLS + c] != expect) {
|
||||
printf("transpose mismatch at (%d,%d): got %g expected %g\n",
|
||||
r,
|
||||
c,
|
||||
transposed[r * COLS + c],
|
||||
expect);
|
||||
tp_ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tp_ok) {
|
||||
printf("shared_tile_transpose kernel: OK (tile transpose via cuda::shared_memory_mdspan)\n");
|
||||
}
|
||||
|
||||
auto dl_wrapper = cuda::to_dlpack_tensor(out_md);
|
||||
const auto &dltensor = dl_wrapper.get();
|
||||
printf("\ncuda::to_dlpack_tensor metadata:\n");
|
||||
printf(" device : kDLCUDA (ordinal %d)\n", dltensor.device.device_id);
|
||||
printf(" ndim : %d\n", dltensor.ndim);
|
||||
printf(" dtype : code=%u bits=%u lanes=%u\n",
|
||||
static_cast<unsigned>(dltensor.dtype.code),
|
||||
static_cast<unsigned>(dltensor.dtype.bits),
|
||||
static_cast<unsigned>(dltensor.dtype.lanes));
|
||||
printf(" shape : [%lld, %lld]\n",
|
||||
static_cast<long long>(dltensor.shape[0]),
|
||||
static_cast<long long>(dltensor.shape[1]));
|
||||
if (dltensor.strides != nullptr) {
|
||||
printf(" strides : [%lld, %lld]\n",
|
||||
static_cast<long long>(dltensor.strides[0]),
|
||||
static_cast<long long>(dltensor.strides[1]));
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaFree(d_in));
|
||||
checkCudaErrors(cudaFree(d_out));
|
||||
|
||||
if (!scale_ok || !tp_ok) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("\nDone\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
65
cpp/4_CUDA_Libraries/libcuxxRandom/CMakeLists.txt
Normal file
65
cpp/4_CUDA_Libraries/libcuxxRandom/CMakeLists.txt
Normal file
@@ -0,0 +1,65 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(libcuxxRandom LANGUAGES C CXX CUDA)
|
||||
|
||||
# Disable response file for libraries on QNX as qcc does not support lib paths with double quotes
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Fetch CCCL (CUB + libcu++ + Thrust) via CPM. The toolkit that ships
|
||||
# with CUDA 13.2 bundles CCCL 3.2, but this sample uses APIs added in
|
||||
# CCCL 3.3. Pinning the tag here lets the sample build on any toolkit
|
||||
# with a usable nvcc. Override with -DCCCL_SOURCE_DIR=/path/to/cccl
|
||||
# to use a local checkout instead of fetching from GitHub.
|
||||
set(CCCL_SAMPLES_CCCL_TAG "v3.3.3" CACHE STRING
|
||||
"Tag/branch of NVIDIA/cccl to fetch for the CCCL samples")
|
||||
|
||||
if(NOT TARGET CCCL::CCCL)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CPM.cmake")
|
||||
if(DEFINED CCCL_SOURCE_DIR AND NOT CCCL_SOURCE_DIR STREQUAL "")
|
||||
CPMAddPackage(NAME CCCL SOURCE_DIR "${CCCL_SOURCE_DIR}")
|
||||
else()
|
||||
CPMAddPackage(
|
||||
NAME CCCL
|
||||
GIT_REPOSITORY "https://github.com/NVIDIA/cccl"
|
||||
GIT_TAG "${CCCL_SAMPLES_CCCL_TAG}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for libcuxxRandom
|
||||
add_executable(libcuxxRandom libcuxxRandom.cu)
|
||||
|
||||
target_compile_options(libcuxxRandom PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(libcuxxRandom PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(libcuxxRandom PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(libcuxxRandom PRIVATE
|
||||
CUDA::cudart
|
||||
CCCL::CCCL
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
44
cpp/4_CUDA_Libraries/libcuxxRandom/README.md
Normal file
44
cpp/4_CUDA_Libraries/libcuxxRandom/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# libcuxxRandom - libcu++ Random Distributions
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates the random-number facilities added to libcu++ in CCCL. `<cuda/std/random>` now offers host- and device-compatible implementations of the standard C++ distributions (uniform, normal, Poisson, Bernoulli, and more) and backports the C++26 `cuda::std::philox4x32` / `philox4x64` engines. `<cuda/random>` adds `cuda::pcg64` as an NVIDIA extension (the same generator NumPy uses by default). A kernel draws samples on each thread and the host computes empirical statistics, comparing them to the theoretical mean / variance / probability.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CCCL 3.3, libcu++ Random, PCG, Philox, Device-Side PRNG
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus) [SM 10.0 ](https://developer.nvidia.com/cuda-gpus) [SM 11.0 ](https://developer.nvidia.com/cuda-gpus) [SM 12.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CCCL libcu++](https://nvidia.github.io/cccl/libcudacxx/)
|
||||
|
||||
cuda::pcg64, cuda::std::philox4x32, cuda::std::uniform_real_distribution, cuda::std::normal_distribution, cuda::std::poisson_distribution, cuda::std::bernoulli_distribution
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
|
||||
cudaMalloc, cudaFree, cudaMemcpy, cudaDeviceSynchronize, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
|
||||
[CCCL 3.3+](https://github.com/NVIDIA/cccl). Fetched automatically via CPM at configure time (pinned to `v3.3.3`). Override with `-DCCCL_SOURCE_DIR=/path/to/cccl` to use a local checkout.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[CCCL 3.3 release notes](https://github.com/NVIDIA/cccl/releases), [cuda::pcg64 header](https://github.com/NVIDIA/cccl/blob/main/libcudacxx/include/cuda/__random/pcg_engine.h), [NumPy PCG64](https://numpy.org/doc/stable/reference/random/bit_generators/pcg64.html)
|
||||
180
cpp/4_CUDA_Libraries/libcuxxRandom/libcuxxRandom.cu
Normal file
180
cpp/4_CUDA_Libraries/libcuxxRandom/libcuxxRandom.cu
Normal file
@@ -0,0 +1,180 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This sample demonstrates the random-number facilities added to libcu++
|
||||
* in CCCL 3.3: <cuda/std/random> now offers host- and device-compatible
|
||||
* implementations of the standard C++ distributions (uniform, normal,
|
||||
* Poisson, Bernoulli, ...), and backports the C++26 Philox counter-based
|
||||
* engines. <cuda/random> adds cuda::pcg64 as an NVIDIA extension (the
|
||||
* same generator NumPy uses by default).
|
||||
*
|
||||
* A kernel draws many samples from four different distributions on each
|
||||
* thread and the host computes empirical summary statistics, comparing
|
||||
* them to the theoretical mean / variance / probability.
|
||||
*/
|
||||
|
||||
/* Includes, system */
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
/* Includes, cuda */
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
/* Includes, cccl */
|
||||
#include <cuda/random>
|
||||
#include <cuda/std/random>
|
||||
|
||||
#define THREADS_PER_BLOCK 256
|
||||
#define SAMPLES_PER_THREAD 256
|
||||
|
||||
/* Per-thread kernel: seed a PCG engine, draw samples from four
|
||||
* distributions, and also pull Philox output through a Bernoulli dist
|
||||
* to show that distributions work with any engine. */
|
||||
__global__ void sample_kernel(unsigned long long base_seed,
|
||||
int num_samples_per_thread,
|
||||
float *uniform_out,
|
||||
float *normal_out,
|
||||
int *poisson_out,
|
||||
int *bernoulli_out)
|
||||
{
|
||||
const int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int total_threads = gridDim.x * blockDim.x;
|
||||
|
||||
cuda::pcg64 rng(base_seed + static_cast<unsigned long long>(tid));
|
||||
|
||||
cuda::std::uniform_real_distribution<float> uniform_dist(0.0f, 1.0f);
|
||||
cuda::std::normal_distribution<float> normal_dist(0.0f, 1.0f);
|
||||
cuda::std::poisson_distribution<int> poisson_dist(4.0);
|
||||
cuda::std::bernoulli_distribution bernoulli_dist(0.25);
|
||||
|
||||
cuda::std::philox4x32 philox(static_cast<cuda::std::uint32_t>(base_seed + 17u + tid));
|
||||
|
||||
for (int i = 0; i < num_samples_per_thread; ++i) {
|
||||
const int idx = i * total_threads + tid;
|
||||
uniform_out[idx] = uniform_dist(rng);
|
||||
normal_out[idx] = normal_dist(rng);
|
||||
poisson_out[idx] = poisson_dist(rng);
|
||||
bernoulli_out[idx] = bernoulli_dist(philox) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void summarize(const std::vector<T> &samples, double expected_mean, double expected_var, const char *label)
|
||||
{
|
||||
const size_t n = samples.size();
|
||||
double sum = 0.0;
|
||||
for (const auto v : samples)
|
||||
sum += static_cast<double>(v);
|
||||
const double mean = sum / static_cast<double>(n);
|
||||
double sq = 0.0;
|
||||
for (const auto v : samples) {
|
||||
const double d = static_cast<double>(v) - mean;
|
||||
sq += d * d;
|
||||
}
|
||||
const double var = sq / static_cast<double>(n - 1);
|
||||
printf("%-24s n=%zu mean=%.4f (exp %.4f) var=%.4f (exp %.4f)\n",
|
||||
label,
|
||||
n,
|
||||
mean,
|
||||
expected_mean,
|
||||
var,
|
||||
expected_var);
|
||||
}
|
||||
|
||||
static void summarize_bernoulli(const std::vector<int> &samples, double expected_p)
|
||||
{
|
||||
long long ones = 0;
|
||||
for (int v : samples)
|
||||
ones += v;
|
||||
const double p = static_cast<double>(ones) / static_cast<double>(samples.size());
|
||||
printf("%-24s n=%zu p(1)=%.4f (exp %.4f)\n", "bernoulli(0.25):", samples.size(), p, expected_p);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int num_blocks = 64;
|
||||
for (int i = 1; i + 1 < argc; ++i) {
|
||||
if (strcmp(argv[i], "--blocks") == 0)
|
||||
num_blocks = atoi(argv[i + 1]);
|
||||
}
|
||||
if (num_blocks <= 0)
|
||||
num_blocks = 1;
|
||||
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
cudaDeviceProp props;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&props, devID));
|
||||
printf("Device: %s (Compute Capability %d.%d)\n\n", props.name, props.major, props.minor);
|
||||
|
||||
const int total_threads = num_blocks * THREADS_PER_BLOCK;
|
||||
const size_t n = static_cast<size_t>(total_threads) * SAMPLES_PER_THREAD;
|
||||
printf("Drawing %zu samples per distribution (%d blocks x %d threads x %d samples/thread)\n\n",
|
||||
n,
|
||||
num_blocks,
|
||||
THREADS_PER_BLOCK,
|
||||
SAMPLES_PER_THREAD);
|
||||
|
||||
float *d_uniform = nullptr;
|
||||
float *d_normal = nullptr;
|
||||
int *d_poisson = nullptr;
|
||||
int *d_bernoulli = nullptr;
|
||||
checkCudaErrors(cudaMalloc(&d_uniform, n * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc(&d_normal, n * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc(&d_poisson, n * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc(&d_bernoulli, n * sizeof(int)));
|
||||
|
||||
const unsigned long long seed = 0xC0FFEE00ULL;
|
||||
sample_kernel<<<num_blocks, THREADS_PER_BLOCK>>>(
|
||||
seed, SAMPLES_PER_THREAD, d_uniform, d_normal, d_poisson, d_bernoulli);
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
std::vector<float> uniform(n), normal(n);
|
||||
std::vector<int> poisson(n), bernoulli(n);
|
||||
checkCudaErrors(cudaMemcpy(uniform.data(), d_uniform, n * sizeof(float), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaMemcpy(normal.data(), d_normal, n * sizeof(float), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaMemcpy(poisson.data(), d_poisson, n * sizeof(int), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaMemcpy(bernoulli.data(), d_bernoulli, n * sizeof(int), cudaMemcpyDeviceToHost));
|
||||
|
||||
summarize(uniform, /*mean=*/0.5, /*var=*/1.0 / 12.0, "uniform(0,1):");
|
||||
summarize(normal, /*mean=*/0.0, /*var=*/1.0, "normal(0,1):");
|
||||
summarize(poisson, /*mean=*/4.0, /*var=*/4.0, "poisson(lambda=4):");
|
||||
summarize_bernoulli(bernoulli, /*p=*/0.25);
|
||||
|
||||
printf("\nEngines exercised: cuda::pcg64 (NumPy-compatible) and cuda::std::philox4x32 (C++26)\n");
|
||||
|
||||
checkCudaErrors(cudaFree(d_uniform));
|
||||
checkCudaErrors(cudaFree(d_normal));
|
||||
checkCudaErrors(cudaFree(d_poisson));
|
||||
checkCudaErrors(cudaFree(d_bernoulli));
|
||||
|
||||
printf("Done\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user