mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-13 00:08:26 +08:00
CUDA 13.4 samples update - v13.4-public
Release 13.4 of the CUDA Samples supported by CUDA Toolkit 13.4. See Changelog for more information.
This commit is contained in:
@@ -1,44 +1,35 @@
|
||||
add_subdirectory(UnifiedMemoryStreams)
|
||||
add_subdirectory(asyncAPI)
|
||||
add_subdirectory(clock)
|
||||
add_subdirectory(clock_nvrtc)
|
||||
add_subdirectory(cudaOpenMP)
|
||||
add_subdirectory(fp16ScalarProduct)
|
||||
add_subdirectory(matrixMul)
|
||||
add_subdirectory(matrixMulDrv)
|
||||
add_subdirectory(matrixMulDynlinkJIT)
|
||||
add_subdirectory(matrixMul_nvrtc)
|
||||
add_subdirectory(mergeSort)
|
||||
add_subdirectory(simpleAWBarrier)
|
||||
add_subdirectory(simpleAssert)
|
||||
add_subdirectory(simpleAssert_nvrtc)
|
||||
add_subdirectory(simpleAtomicIntrinsics)
|
||||
add_subdirectory(simpleAtomicIntrinsics_nvrtc)
|
||||
add_subdirectory(simpleAttributes)
|
||||
add_subdirectory(simpleCUDA2GL)
|
||||
add_subdirectory(simpleCallback)
|
||||
add_subdirectory(simpleCooperativeGroups)
|
||||
add_subdirectory(simpleCubemapTexture)
|
||||
add_subdirectory(simpleDrvRuntime)
|
||||
add_subdirectory(simpleHyperQ)
|
||||
add_subdirectory(simpleIPC)
|
||||
add_subdirectory(simpleLayeredTexture)
|
||||
add_subdirectory(simpleMPI)
|
||||
add_subdirectory(simpleMultiCopy)
|
||||
add_subdirectory(simpleMultiGPU)
|
||||
add_subdirectory(simpleOccupancy)
|
||||
add_subdirectory(simpleP2P)
|
||||
add_subdirectory(simplePitchLinearTexture)
|
||||
add_subdirectory(simplePrintf)
|
||||
add_subdirectory(simpleStreams)
|
||||
add_subdirectory(simpleSurfaceWrite)
|
||||
add_subdirectory(simpleTemplates)
|
||||
add_subdirectory(simpleTexture)
|
||||
add_subdirectory(simpleTexture3D)
|
||||
add_subdirectory(simpleTextureDrv)
|
||||
add_subdirectory(simpleVoteIntrinsics)
|
||||
add_subdirectory(simpleZeroCopy)
|
||||
add_subdirectory(template)
|
||||
add_subdirectory(systemWideAtomics)
|
||||
add_subdirectory(vectorAdd)
|
||||
add_subdirectory(vectorAddDrv)
|
||||
|
||||
@@ -7,9 +7,6 @@ This sample illustrates the usage of CUDA events for both GPU timing and overlap
|
||||
### [clock](./clock)
|
||||
This example shows how to use the clock function to measure the performance of block of threads of a kernel accurately.
|
||||
|
||||
### [clock_nvrtc](./clock_nvrtc)
|
||||
This example shows how to use the clock function using libNVRTC to measure the performance of block of threads of a kernel accurately.
|
||||
|
||||
### [cudaOpenMP](./cudaOpenMP)
|
||||
This sample demonstrates how to use OpenMP API to write an application for multiple GPUs.
|
||||
|
||||
@@ -19,30 +16,18 @@ Calculates scalar product of two vectors of FP16 numbers.
|
||||
### [matrixMul](./matrixMul)
|
||||
This sample implements matrix multiplication and is exactly the same as Chapter 6 of the programming guide. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. To illustrate GPU performance for matrix multiply, this sample also shows how to use the new CUDA 4.0 interface for CUBLAS to demonstrate high-performance performance for matrix multiplication.
|
||||
|
||||
### [matrixMul_nvrtc](./matrixMul_nvrtc)
|
||||
This sample implements matrix multiplication and is exactly the same as Chapter 6 of the programming guide. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. To illustrate GPU performance for matrix multiply, this sample also shows how to use the new CUDA 4.0 interface for CUBLAS to demonstrate high-performance performance for matrix multiplication.
|
||||
|
||||
### [matrixMulDrv](./matrixMulDrv)
|
||||
This sample implements matrix multiplication and uses the new CUDA 4.0 kernel launch Driver API. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. CUBLAS provides high-performance matrix multiplication.
|
||||
|
||||
### [matrixMulDynlinkJIT](./matrixMulDynlinkJIT)
|
||||
This sample revisits matrix multiplication using the CUDA driver API. It demonstrates how to link to CUDA driver at runtime and how to use JIT (just-in-time) compilation from PTX code. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. CUBLAS provides high-performance matrix multiplication.
|
||||
|
||||
### [mergeSort](./mergeSort)
|
||||
This sample implements a merge sort (also known as Batcher's sort), algorithms belonging to the class of sorting networks. While generally subefficient on large sequences compared to algorithms with better asymptotic algorithmic complexity (i.e. merge sort or radix sort), may be the algorithms of choice for sorting batches of short- to mid-sized (key, value) array pairs. Refer to the excellent tutorial by H. W. Lang http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/indexen.htm
|
||||
|
||||
### [simpleAssert](./simpleAssert)
|
||||
This CUDA Runtime API sample is a very basic sample that implements how to use the assert function in the device code. Requires Compute Capability 2.0 .
|
||||
|
||||
### [simpleAssert_nvrtc](./simpleAssert_nvrtc)
|
||||
This CUDA Runtime API sample is a very basic sample that implements how to use the assert function in the device code. Requires Compute Capability 2.0 .
|
||||
|
||||
### [simpleAtomicIntrinsics](./simpleAtomicIntrinsics)
|
||||
A simple demonstration of global memory atomic instructions.
|
||||
|
||||
### [simpleAtomicIntrinsics_nvrtc](./simpleAtomicIntrinsics_nvrtc)
|
||||
A simple demonstration of global memory atomic instructions.This sample makes use of NVRTC for Runtime Compilation.
|
||||
|
||||
### [simpleAttributes](./simpleAttributes)
|
||||
This CUDA Runtime API sample is a very basic example that implements how to use the stream attributes that affect L2 locality. Performance improvement due to use of L2 access policy window can only be noticed on Compute capability 8.0 or higher.
|
||||
|
||||
@@ -64,9 +49,6 @@ This sample shows how to copy CUDA image back to OpenGL using the most efficient
|
||||
### [simpleDrvRuntime](./simpleDrvRuntime)
|
||||
A simple example which demonstrates how CUDA Driver and Runtime APIs can work together to load cuda fatbinary of vector add kernel and performing vector addition.
|
||||
|
||||
### [simpleHyperQ](./simpleHyperQ)
|
||||
This sample demonstrates the use of CUDA streams for concurrent execution of several kernels on devices which provide HyperQ (SM 3.5). Devices without HyperQ (SM 2.0 and SM 3.0) will run a maximum of two kernels concurrently.
|
||||
|
||||
### [simpleIPC](./simpleIPC)
|
||||
This CUDA Runtime API sample is a very basic sample that demonstrates Inter Process Communication with one process per GPU for computation. Requires Compute Capability 3.0 or higher and a Linux Operating System, or a Windows Operating System with TCC enabled GPUs
|
||||
|
||||
@@ -82,9 +64,6 @@ Supported in GPUs with Compute Capability 1.1, overlapping compute with one memc
|
||||
### [simpleMultiGPU](./simpleMultiGPU)
|
||||
This application demonstrates how to use the new CUDA 4.0 API for CUDA context management and multi-threaded access to run CUDA kernels on multiple-GPUs.
|
||||
|
||||
### [simpleOccupancy](./simpleOccupancy)
|
||||
This sample demonstrates the basic usage of the CUDA occupancy calculator and occupancy-based launch configurator APIs by launching a kernel with the launch configurator, and measures the utilization difference against a manually configured launch.
|
||||
|
||||
### [simpleP2P](./simpleP2P)
|
||||
This application demonstrates CUDA APIs that support Peer-To-Peer (P2P) copies, Peer-To-Peer (P2P) addressing, and Unified Virtual Memory Addressing (UVA) between multiple GPUs. In general, P2P is supported between two same GPUs with some exceptions, such as some Tesla and Quadro GPUs.
|
||||
|
||||
@@ -100,9 +79,6 @@ This sample uses CUDA streams to overlap kernel executions with memory copies be
|
||||
### [simpleSurfaceWrite](./simpleSurfaceWrite)
|
||||
Simple example that demonstrates the use of 2D surface references (Write-to-Texture)
|
||||
|
||||
### [simpleTemplates](./simpleTemplates)
|
||||
This sample is a templatized version of the template project. It also shows how to correctly templatize dynamically allocated shared memory arrays.
|
||||
|
||||
### [simpleTexture](./simpleTexture)
|
||||
Simple example that demonstrates use of Textures in CUDA.
|
||||
|
||||
@@ -121,9 +97,6 @@ This sample illustrates how to use Zero MemCopy, kernels can read and write dire
|
||||
### [systemWideAtomics](./systemWideAtomics)
|
||||
A simple demonstration of system wide atomic instructions.
|
||||
|
||||
### [template](./template)
|
||||
A trivial template project that can be used as a starting point to create new CUDA projects.
|
||||
|
||||
### [UnifiedMemoryStreams](./UnifiedMemoryStreams)
|
||||
This sample demonstrates the use of OpenMP and streams with Unified Memory on a single GPU.
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(UnifiedMemoryStreams LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# This sample is not supported on QNX
|
||||
@@ -31,23 +27,15 @@ endif()
|
||||
find_package(OpenMP COMPONENTS CXX)
|
||||
|
||||
if(OpenMP_CXX_FOUND)
|
||||
# Add target for UnifiedMemoryStreams
|
||||
add_executable(UnifiedMemoryStreams UnifiedMemoryStreams.cu)
|
||||
|
||||
target_compile_options(UnifiedMemoryStreams PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(UnifiedMemoryStreams PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(UnifiedMemoryStreams PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_compile_options(UnifiedMemoryStreams PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
target_compile_features(UnifiedMemoryStreams PRIVATE cxx_std_17 cuda_std_17)
|
||||
target_link_libraries(UnifiedMemoryStreams PUBLIC
|
||||
CUDA::cublas
|
||||
OpenMP::OpenMP_CXX
|
||||
)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
else()
|
||||
message(STATUS "OpenMP not found - will not build sample 'UnifiedMemoryStreams'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(asyncAPI LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for asyncAPI
|
||||
add_executable(asyncAPI asyncAPI.cu)
|
||||
|
||||
target_compile_options(asyncAPI PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(asyncAPI PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(asyncAPI PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(clock LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for asyncAPI
|
||||
add_executable(clock clock.cu)
|
||||
|
||||
target_compile_options(clock PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(clock PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(clock PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,32 +1,79 @@
|
||||
# clock - Clock
|
||||
# clock - Per-Block Kernel Timing with CUB Reduction
|
||||
|
||||
## Description
|
||||
|
||||
This example shows how to use the clock function to measure the performance of block of threads of a kernel accurately.
|
||||
A CUDA sample that demonstrates how to use the `clock()` function to accurately measure kernel execution time on a per-block basis. Each of the 64 blocks records its own start and end SM cycle counter, then performs a block-wide parallel **min-reduction** over 512 float elements using CUB's `BlockReduce`. The host collects all timestamps and computes the average elapsed clock cycles across all blocks.
|
||||
|
||||
Because blocks execute in parallel and out of order with no cross-block synchronization, each block independently measures its own execution time — this is the correct way to time GPU work at block granularity.
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
- Using `clock()` inside a CUDA kernel to capture per-block SM cycle counts
|
||||
- Performing a block-wide parallel reduction with `cub::BlockReduce` using a custom binary operator
|
||||
- Loading multiple elements per thread (`ITEMS_PER_THREAD = 2`) for CUB reductions
|
||||
- Understanding why only thread 0 holds the valid aggregate after a `BlockReduce`
|
||||
- Querying device properties (`cudaDeviceGetAttribute`) without helper libraries
|
||||
- Computing average elapsed clocks on the host from per-block timestamps
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Performance Strategies
|
||||
- **SM Clock Counter** — `clock()` reads the streaming multiprocessor's cycle counter; difference between two samples gives elapsed cycles for that block
|
||||
- **CUB BlockReduce** — warp-shuffle-based block-scope reduction; default constructor allocates shared memory internally via `PrivateStorage()`, no explicit `TempStorage` needed
|
||||
- **Items per thread** — each thread owns 2 elements; CUB's array overload of `Reduce` combines them before the cross-thread reduction
|
||||
- **Per-block timing** — since blocks run independently, each block times itself; the host averages results across all blocks
|
||||
|
||||
## Supported SM Architectures
|
||||
## Key APIs
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
### CUDA Runtime
|
||||
- `cudaSetDevice` — select the active GPU for all subsequent CUDA calls
|
||||
- `cudaDeviceGetAttribute` — query device properties (compute capability, SM count) without `cudaGetDeviceProperties`
|
||||
- `cudaMalloc` / `cudaFree` — allocate and release device memory
|
||||
- `cudaMemcpy` — transfer data between host and device
|
||||
- `clock()` — device-side SM cycle counter (returns `clock_t`)
|
||||
|
||||
## Supported OSes
|
||||
### CUB
|
||||
- `cub::BlockReduce<T, BLOCK_THREADS>` — block-scope reduction template
|
||||
- `BlockReduce::Reduce(T (&input)[ITEMS_PER_THREAD], ReductionOp op)` — reduce multiple items per thread with a custom binary operator
|
||||
|
||||
Linux, Windows
|
||||
## Requirements
|
||||
|
||||
## Supported CPU Architecture
|
||||
### Hardware
|
||||
- NVIDIA GPU with Compute Capability 7.5 or higher
|
||||
|
||||
x86_64, armv7l
|
||||
### Software
|
||||
- CMake 3.20 or newer
|
||||
- A C++17-capable host compiler
|
||||
|
||||
## CUDA APIs involved
|
||||
## How to Build
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMalloc, cudaMemcpy, cudaFree
|
||||
See the [top-level README](../../../README.md#building-cuda-samples) for full build instructions, including how to build all samples or a single sample standalone.
|
||||
|
||||
## Prerequisites
|
||||
## How to Run
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
```bash
|
||||
./clock
|
||||
```
|
||||
|
||||
## References (for more details)
|
||||
No command-line arguments are required. The sample always runs on device 0.
|
||||
|
||||
## Expected Output
|
||||
|
||||
```text
|
||||
CUDA Clock sample
|
||||
GPU Device 0: with compute capability 8.9 and Number of SMs 142
|
||||
|
||||
Average clocks/block = 1239.640625
|
||||
```
|
||||
|
||||
The average clocks value varies by GPU and reflects how many SM cycles each block takes to complete the reduction. Blocks scheduled later on a busy GPU will show higher elapsed times.
|
||||
|
||||
## Files
|
||||
|
||||
- `clock.cu` — kernel implementation and host driver
|
||||
- `README.md` — this file
|
||||
- `CMakeLists.txt` — build configuration
|
||||
|
||||
## See Also
|
||||
|
||||
- [CUB BlockReduce documentation](https://nvidia.github.io/cccl/unstable/cub/developer/block_scope.html)
|
||||
- [CUDA C++ Programming Guide — clock()](https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/cpp-language-support.html#clock-and-clock64)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -41,45 +41,36 @@
|
||||
// CUDA runtime
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
// helper functions and utilities to work with CUDA
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_functions.h>
|
||||
// CUB for block-scope reduction
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
#define NUM_BLOCKS 64
|
||||
#define NUM_THREADS 256
|
||||
|
||||
// This kernel computes a standard parallel reduction and evaluates the
|
||||
// time it takes to do that for each block. The timing results are stored
|
||||
// in device memory.
|
||||
__global__ static void timedReduction(const float *input, float *output, clock_t *timer)
|
||||
{
|
||||
// __shared__ float shared[2 * blockDim.x];
|
||||
extern __shared__ float shared[];
|
||||
|
||||
const int tid = threadIdx.x;
|
||||
const int bid = blockIdx.x;
|
||||
|
||||
if (tid == 0)
|
||||
timer[bid] = clock();
|
||||
|
||||
// Copy input.
|
||||
shared[tid] = input[tid];
|
||||
shared[tid + blockDim.x] = input[tid + blockDim.x];
|
||||
// Each thread loads 2 elements and reduces them to a local min.
|
||||
float thread_data[2];
|
||||
thread_data[0] = input[tid];
|
||||
thread_data[1] = input[tid + blockDim.x];
|
||||
|
||||
// Perform reduction to find minimum.
|
||||
for (int d = blockDim.x; d > 0; d /= 2) {
|
||||
__syncthreads();
|
||||
// Block-wide min-reduction using CUB. Default constructor allocates
|
||||
// shared memory internally via PrivateStorage().
|
||||
using BlockReduce = cub::BlockReduce<float, NUM_THREADS>;
|
||||
float block_min = BlockReduce().Reduce(thread_data, [] __device__(float a, float b) { return fminf(a, b); });
|
||||
|
||||
if (tid < d) {
|
||||
float f0 = shared[tid];
|
||||
float f1 = shared[tid + d];
|
||||
|
||||
if (f1 < f0) {
|
||||
shared[tid] = f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write result.
|
||||
// Only thread 0 holds the valid aggregate.
|
||||
if (tid == 0)
|
||||
output[bid] = shared[0];
|
||||
output[bid] = block_min;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
@@ -87,33 +78,25 @@ __global__ static void timedReduction(const float *input, float *output, clock_t
|
||||
timer[bid + gridDim.x] = clock();
|
||||
}
|
||||
|
||||
#define NUM_BLOCKS 64
|
||||
#define NUM_THREADS 256
|
||||
|
||||
// It's interesting to change the number of blocks and the number of threads to
|
||||
// understand how to keep the hardware busy.
|
||||
//
|
||||
// Here are some numbers I get on my G80:
|
||||
// blocks - clocks
|
||||
// 1 - 3096
|
||||
// 8 - 3232
|
||||
// 16 - 3364
|
||||
// 32 - 4615
|
||||
// 64 - 9981
|
||||
//
|
||||
// With less than 16 blocks some of the multiprocessors of the device are idle.
|
||||
// With more than 16 you are using all the multiprocessors, but there's only one
|
||||
// block per multiprocessor and that doesn't allow you to hide the latency of
|
||||
// the memory. With more than 32 the speed scales linearly.
|
||||
|
||||
// Start the main CUDA Sample here
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("CUDA Clock sample\n");
|
||||
|
||||
// This will pick the best possible CUDA capable device
|
||||
int dev = findCudaDevice(argc, (const char **)argv);
|
||||
// Select device 0 as the active GPU
|
||||
int devID = 0;
|
||||
cudaSetDevice(devID);
|
||||
|
||||
// Query compute capability (major.minor) and number of SMs on the device
|
||||
int major = 0, minor = 0, smCount = 0;
|
||||
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devID);
|
||||
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, devID);
|
||||
cudaDeviceGetAttribute(&smCount, cudaDevAttrMultiProcessorCount, devID);
|
||||
|
||||
// Print device info
|
||||
printf("GPU Device %d: with compute capability %d.%d and Number of SMs %d\n\n", devID, major, minor, smCount);
|
||||
|
||||
// Device pointers for input data, per-block minimum output, and clock timestamps
|
||||
float *dinput = NULL;
|
||||
float *doutput = NULL;
|
||||
clock_t *dtimer = NULL;
|
||||
@@ -125,19 +108,19 @@ int main(int argc, char **argv)
|
||||
input[i] = (float)i;
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&dinput, sizeof(float) * NUM_THREADS * 2));
|
||||
checkCudaErrors(cudaMalloc((void **)&doutput, sizeof(float) * NUM_BLOCKS));
|
||||
checkCudaErrors(cudaMalloc((void **)&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));
|
||||
cudaMalloc((void **)&dinput, sizeof(float) * NUM_THREADS * 2);
|
||||
cudaMalloc((void **)&doutput, sizeof(float) * NUM_BLOCKS);
|
||||
cudaMalloc((void **)&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2);
|
||||
|
||||
checkCudaErrors(cudaMemcpy(dinput, input, sizeof(float) * NUM_THREADS * 2, cudaMemcpyHostToDevice));
|
||||
cudaMemcpy(dinput, input, sizeof(float) * NUM_THREADS * 2, cudaMemcpyHostToDevice);
|
||||
|
||||
timedReduction<<<NUM_BLOCKS, NUM_THREADS, sizeof(float) * 2 * NUM_THREADS>>>(dinput, doutput, dtimer);
|
||||
timedReduction<<<NUM_BLOCKS, NUM_THREADS>>>(dinput, doutput, dtimer);
|
||||
|
||||
checkCudaErrors(cudaMemcpy(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2, cudaMemcpyDeviceToHost));
|
||||
cudaMemcpy(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2, cudaMemcpyDeviceToHost);
|
||||
|
||||
checkCudaErrors(cudaFree(dinput));
|
||||
checkCudaErrors(cudaFree(doutput));
|
||||
checkCudaErrors(cudaFree(dtimer));
|
||||
cudaFree(dinput);
|
||||
cudaFree(doutput);
|
||||
cudaFree(dtimer);
|
||||
|
||||
long double avgElapsedClocks = 0;
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(clock_nvrtc LANGUAGES C CXX CUDA)
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add sample target executable
|
||||
add_executable(clock_nvrtc clock.cpp)
|
||||
|
||||
target_compile_options(clock_nvrtc PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(clock_nvrtc PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
target_link_libraries(clock_nvrtc PRIVATE
|
||||
CUDA::nvrtc
|
||||
CUDA::cuda_driver
|
||||
)
|
||||
|
||||
# Copy clock_kernel.cu to the output directory
|
||||
add_custom_command(TARGET clock_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clock_kernel.cu ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
@@ -1,39 +0,0 @@
|
||||
# clock_nvrtc - Clock libNVRTC
|
||||
|
||||
## Description
|
||||
|
||||
This example shows how to use the clock function using libNVRTC to measure the performance of block of threads of a kernel accurately.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Performance Strategies, Runtime Compilation
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows, QNX
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuMemcpyDtoH, cuLaunchKernel, cuMemcpyHtoD, cuCtxSynchronize, cuMemAlloc, cuMemFree, cuModuleGetFunction
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaBlockSize, cudaGridSize
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[NVRTC](../../../README.md#nvrtc)
|
||||
|
||||
## 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)
|
||||
@@ -1,137 +0,0 @@
|
||||
/* Copyright (c) 2022, 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 example shows how to use the clock function to measure the performance
|
||||
* of block of threads of a kernel accurately. Blocks are executed in parallel
|
||||
* and out of order. Since there's no synchronization mechanism between blocks,
|
||||
* we measure the clock once for each block. The clock samples are written to
|
||||
* device memory.
|
||||
*/
|
||||
|
||||
// System includes
|
||||
#include <assert.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <nvrtc_helper.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// helper functions and utilities to work with CUDA
|
||||
#include <helper_functions.h>
|
||||
|
||||
#define NUM_BLOCKS 64
|
||||
|
||||
#define NUM_THREADS 256
|
||||
|
||||
// It's interesting to change the number of blocks and the number of threads to
|
||||
// understand how to keep the hardware busy.
|
||||
//
|
||||
|
||||
// Here are some numbers I get on my G80:
|
||||
// blocks - clocks
|
||||
// 1 - 3096
|
||||
// 8 - 3232
|
||||
// 16 - 3364
|
||||
// 32 - 4615
|
||||
// 64 - 9981
|
||||
|
||||
//
|
||||
// With less than 16 blocks some of the multiprocessors of the device are idle.
|
||||
// With
|
||||
// more than 16 you are using all the multiprocessors, but there's only one
|
||||
// block per
|
||||
// multiprocessor and that doesn't allow you to hide the latency of the memory.
|
||||
// With
|
||||
// more than 32 the speed scales linearly.
|
||||
|
||||
// Start the main CUDA Sample here
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("CUDA Clock sample\n");
|
||||
|
||||
typedef long clock_t;
|
||||
|
||||
clock_t timer[NUM_BLOCKS * 2];
|
||||
|
||||
float input[NUM_THREADS * 2];
|
||||
|
||||
for (int i = 0; i < NUM_THREADS * 2; i++) {
|
||||
input[i] = (float)i;
|
||||
}
|
||||
|
||||
char *cubin, *kernel_file;
|
||||
size_t cubinSize;
|
||||
|
||||
kernel_file = sdkFindFilePath("clock_kernel.cu", argv[0]);
|
||||
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
|
||||
|
||||
CUmodule module = loadCUBIN(cubin, argc, argv);
|
||||
CUfunction kernel_addr;
|
||||
|
||||
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "timedReduction"));
|
||||
|
||||
dim3 cudaBlockSize(NUM_THREADS, 1, 1);
|
||||
dim3 cudaGridSize(NUM_BLOCKS, 1, 1);
|
||||
|
||||
CUdeviceptr dinput, doutput, dtimer;
|
||||
checkCudaErrors(cuMemAlloc(&dinput, sizeof(float) * NUM_THREADS * 2));
|
||||
checkCudaErrors(cuMemAlloc(&doutput, sizeof(float) * NUM_BLOCKS));
|
||||
checkCudaErrors(cuMemAlloc(&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));
|
||||
checkCudaErrors(cuMemcpyHtoD(dinput, input, sizeof(float) * NUM_THREADS * 2));
|
||||
|
||||
void *arr[] = {(void *)&dinput, (void *)&doutput, (void *)&dtimer};
|
||||
|
||||
checkCudaErrors(cuLaunchKernel(kernel_addr,
|
||||
cudaGridSize.x,
|
||||
cudaGridSize.y,
|
||||
cudaGridSize.z, /* grid dim */
|
||||
cudaBlockSize.x,
|
||||
cudaBlockSize.y,
|
||||
cudaBlockSize.z, /* block dim */
|
||||
sizeof(float) * 2 * NUM_THREADS,
|
||||
0, /* shared mem, stream */
|
||||
&arr[0], /* arguments */
|
||||
0));
|
||||
|
||||
checkCudaErrors(cuCtxSynchronize());
|
||||
checkCudaErrors(cuMemcpyDtoH(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));
|
||||
checkCudaErrors(cuMemFree(dinput));
|
||||
checkCudaErrors(cuMemFree(doutput));
|
||||
checkCudaErrors(cuMemFree(dtimer));
|
||||
|
||||
long double avgElapsedClocks = 0;
|
||||
|
||||
for (int i = 0; i < NUM_BLOCKS; i++) {
|
||||
avgElapsedClocks += (long double)(timer[i + NUM_BLOCKS] - timer[i]);
|
||||
}
|
||||
|
||||
avgElapsedClocks = avgElapsedClocks / NUM_BLOCKS;
|
||||
printf("Average clocks/block = %Lf\n", avgElapsedClocks);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/* Copyright (c) 2022, 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 example shows how to use the clock function to measure the performance
|
||||
* of block of threads of a kernel accurately. Blocks are executed in parallel
|
||||
* and out of order. Since there's no synchronization mechanism between blocks,
|
||||
* we measure the clock once for each block. The clock samples are written to
|
||||
* device memory.
|
||||
*/
|
||||
|
||||
// This kernel computes a standard parallel reduction and evaluates the
|
||||
// time it takes to do that for each block. The timing results are stored
|
||||
// in device memory.
|
||||
|
||||
extern "C" __global__ void timedReduction(const float *input, float *output, clock_t *timer)
|
||||
{
|
||||
// __shared__ float shared[2 * blockDim.x];
|
||||
extern __shared__ float shared[];
|
||||
|
||||
const int tid = threadIdx.x;
|
||||
const int bid = blockIdx.x;
|
||||
|
||||
if (tid == 0)
|
||||
timer[bid] = clock();
|
||||
|
||||
// Copy input.
|
||||
shared[tid] = input[tid];
|
||||
shared[tid + blockDim.x] = input[tid + blockDim.x];
|
||||
|
||||
// Perform reduction to find minimum.
|
||||
for (int d = blockDim.x; d > 0; d /= 2) {
|
||||
__syncthreads();
|
||||
|
||||
if (tid < d) {
|
||||
float f0 = shared[tid];
|
||||
float f1 = shared[tid + d];
|
||||
|
||||
if (f1 < f0) {
|
||||
shared[tid] = f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write result.
|
||||
if (tid == 0)
|
||||
output[bid] = shared[0];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (tid == 0)
|
||||
timer[bid + gridDim.x] = clock();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,43 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(cudaOpenMP LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
find_package(OpenMP)
|
||||
|
||||
# Source file
|
||||
if(OpenMP_CXX_FOUND)
|
||||
# Add target for asyncAPI
|
||||
add_executable(cudaOpenMP cudaOpenMP.cu)
|
||||
|
||||
target_compile_options(cudaOpenMP PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
target_compile_options(cudaOpenMP PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cudaOpenMP PRIVATE cxx_std_17 cuda_std_17)
|
||||
target_compile_features(cudaOpenMP PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cudaOpenMP PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
target_link_libraries(cudaOpenMP PUBLIC
|
||||
OpenMP::OpenMP_CXX
|
||||
)
|
||||
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
else()
|
||||
message(STATUS "OpenMP not found - will not build sample 'cudaOpenMP'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,33 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(fp16ScalarProduct LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for asyncAPI
|
||||
add_executable(fp16ScalarProduct fp16ScalarProduct.cu)
|
||||
|
||||
target_compile_options(fp16ScalarProduct PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(fp16ScalarProduct PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(fp16ScalarProduct PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(matrixMul LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for asyncAPI
|
||||
add_executable(matrixMul matrixMul.cu)
|
||||
|
||||
target_compile_options(matrixMul PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(matrixMul PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(matrixMul PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements matrix multiplication and is exactly the same as the second example of the [Shared Memory](https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#shared-memory) section of the programming guide. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. To illustrate GPU performance for matrix multiply, this sample also shows how to use the CUDA 4.0+ interface for cuBLAS to demonstrate high-performance performance for matrix multiplication.
|
||||
This sample implements matrix multiplication. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. To illustrate GPU performance for matrix multiply, this sample also shows how to use the CUDA 4.0+ interface for cuBLAS to demonstrate high-performance matrix multiplication.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
@@ -14,7 +14,7 @@ CUDA Runtime API, Linear Algebra
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
Linux, Windows, QNX (QNX Safety cross-build with CUDA Safe toolkit)
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
|
||||
@@ -44,8 +44,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
// CUDA runtime
|
||||
#include <cuda_profiler_api.h>
|
||||
#include <cuda_runtime.h>
|
||||
// The CUDA Safe runtime does not ship the profiler API; gate it on header availability.
|
||||
#if defined(__has_include) && __has_include(<cuda_profiler_api.h>)
|
||||
#include <cuda_profiler_api.h>
|
||||
#define HAVE_CUDA_PROFILER_API 1
|
||||
#endif
|
||||
|
||||
// Helper functions and utilities to work with CUDA
|
||||
#include <helper_cuda.h>
|
||||
@@ -141,11 +145,11 @@ int MatrixMultiply(int argc, char **argv, int block_size, const dim3 &dimsA, con
|
||||
unsigned int size_A = dimsA.x * dimsA.y;
|
||||
unsigned int mem_size_A = sizeof(float) * size_A;
|
||||
float *h_A;
|
||||
checkCudaErrors(cudaMallocHost(&h_A, mem_size_A));
|
||||
checkCudaErrors(cudaMallocHost(&h_A, mem_size_A, 0));
|
||||
unsigned int size_B = dimsB.x * dimsB.y;
|
||||
unsigned int mem_size_B = sizeof(float) * size_B;
|
||||
float *h_B;
|
||||
checkCudaErrors(cudaMallocHost(&h_B, mem_size_B));
|
||||
checkCudaErrors(cudaMallocHost(&h_B, mem_size_B, 0));
|
||||
cudaStream_t stream;
|
||||
|
||||
// Initialize host memory
|
||||
@@ -160,7 +164,7 @@ int MatrixMultiply(int argc, char **argv, int block_size, const dim3 &dimsA, con
|
||||
dim3 dimsC(dimsB.x, dimsA.y, 1);
|
||||
unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);
|
||||
float *h_C;
|
||||
checkCudaErrors(cudaMallocHost(&h_C, mem_size_C));
|
||||
checkCudaErrors(cudaMallocHost(&h_C, mem_size_C, 0));
|
||||
|
||||
if (h_C == NULL) {
|
||||
fprintf(stderr, "Failed to allocate host matrix C!\n");
|
||||
@@ -334,9 +338,13 @@ int main(int argc, char **argv)
|
||||
|
||||
printf("MatrixA(%d,%d), MatrixB(%d,%d)\n", dimsA.x, dimsA.y, dimsB.x, dimsB.y);
|
||||
|
||||
#ifdef HAVE_CUDA_PROFILER_API
|
||||
checkCudaErrors(cudaProfilerStart());
|
||||
#endif
|
||||
int matrix_result = MatrixMultiply(argc, argv, block_size, dimsA, dimsB);
|
||||
#ifdef HAVE_CUDA_PROFILER_API
|
||||
checkCudaErrors(cudaProfilerStop());
|
||||
#endif
|
||||
|
||||
exit(matrix_result);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(matrixMulDrv LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for matrixMulDrv
|
||||
add_executable(matrixMulDrv matrixMulDrv.cpp)
|
||||
|
||||
target_compile_options(matrixMulDrv PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(matrixMulDrv PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(matrixMulDrv PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(matrixMulDrv PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -56,7 +48,7 @@ endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CUDA_FATBIN_FILE}
|
||||
COMMAND ${CMAKE_CUDA_COMPILER} ${INCLUDES} ${ALL_CCFLAGS} -Wno-deprecated-gpu-targets ${GENCODE_FLAGS} -o ${CUDA_FATBIN_FILE} -fatbin ${CUDA_KERNEL_SOURCE}
|
||||
COMMAND ${CMAKE_CUDA_COMPILER} ${INCLUDES} -Wno-deprecated-gpu-targets ${GENCODE_FLAGS} -o ${CUDA_FATBIN_FILE} -fatbin ${CUDA_KERNEL_SOURCE}
|
||||
DEPENDS ${CUDA_KERNEL_SOURCE}
|
||||
COMMENT "Building CUDA fatbin: ${CUDA_FATBIN_FILE}"
|
||||
)
|
||||
@@ -67,6 +59,5 @@ add_custom_target(generate_fatbin_matmulDrv ALL DEPENDS ${CUDA_FATBIN_FILE})
|
||||
# Ensure matrixMulDrv depends on the fatbin
|
||||
add_dependencies(matrixMulDrv generate_fatbin_matmulDrv)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,26 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(matrixMulDynlinkJIT LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for matrixMulDynlinkJIT
|
||||
add_executable(matrixMulDynlinkJIT cuda_drvapi_dynlink.c matrixMulDynlinkJIT.cpp matrixMul_gold.cpp matrixMul_kernel_32_ptxdump.c matrixMul_kernel_64_ptxdump.c)
|
||||
|
||||
target_compile_options(matrixMulDynlinkJIT PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
@@ -28,7 +22,6 @@ target_compile_options(matrixMulDynlinkJIT PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--
|
||||
target_compile_features(matrixMulDynlinkJIT PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(matrixMulDynlinkJIT PROPERTIES
|
||||
CUDA_SEPARABLE_COMPILATION ON
|
||||
POSITION_INDEPENDENT_CODE OFF
|
||||
)
|
||||
|
||||
@@ -46,6 +39,5 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(matrixMulDynlinkJIT PUBLIC dl)
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(matrixMul_nvrtc LANGUAGES C CXX CUDA)
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add sample target executable
|
||||
add_executable(matrixMul_nvrtc matrixMul.cpp)
|
||||
|
||||
target_compile_options(matrixMul_nvrtc PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(matrixMul_nvrtc PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
target_link_libraries(matrixMul_nvrtc PRIVATE
|
||||
CUDA::nvrtc
|
||||
CUDA::cuda_driver
|
||||
)
|
||||
|
||||
# The primary directory of CUDAToolkit_INCLUDE_DIRS is the CUDA Toolkit's include directory for finding the header files.
|
||||
list(GET CUDAToolkit_INCLUDE_DIRS 0 CUDA_INCLUDE_DIR)
|
||||
|
||||
# Copy clock_kernel.cu to the output directory
|
||||
add_custom_command(TARGET matrixMul_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/matrixMul_kernel.cu ${CUDA_INCLUDE_DIR}/cooperative_groups.h ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_custom_command(TARGET matrixMul_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CUDA_INCLUDE_DIR}/cooperative_groups ${CMAKE_CURRENT_BINARY_DIR}/cooperative_groups
|
||||
)
|
||||
|
||||
add_custom_command(TARGET matrixMul_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CUDA_INCLUDE_DIR}/cccl/nv ${CMAKE_CURRENT_BINARY_DIR}/nv
|
||||
)
|
||||
|
||||
add_custom_command(TARGET matrixMul_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CUDA_INCLUDE_DIR}/cccl/cuda ${CMAKE_CURRENT_BINARY_DIR}/cuda
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
@@ -1,36 +0,0 @@
|
||||
# matrixMul_nvrtc - Matrix Multiplication with libNVRTC
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements matrix multiplication and is exactly the same as the second example of the [Shared Memory](https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#shared-memory) section of the programming guide. It has been written for clarity of exposition to illustrate various CUDA programming principles, not with the goal of providing the most performant generic kernel for matrix multiplication. To illustrate GPU performance for matrix multiply, this sample also shows how to use the CUDA 4.0+ interface for cuBLAS to demonstrate high-performance performance for matrix multiplication.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CUDA Runtime API, Linear Algebra, Runtime Compilation
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows, QNX
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuMemcpyDtoH, cuLaunchKernel, cuMemcpyHtoD, cuCtxSynchronize, cuMemAlloc, cuMemFree, cuModuleGetFunction
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[NVRTC](../../../README.md#nvrtc)
|
||||
|
||||
## 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)
|
||||
@@ -1,249 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Matrix multiplication: C = A * B.
|
||||
* Host code.
|
||||
*
|
||||
* This sample implements matrix multiplication as described in Chapter 3
|
||||
* of the programming guide.
|
||||
* It has been written for clarity of exposition to illustrate various CUDA
|
||||
* programming principles, not with the goal of providing the most
|
||||
* performant generic kernel for matrix multiplication.
|
||||
*
|
||||
* See also:
|
||||
* V. Volkov and J. Demmel, "Benchmarking GPUs to tune dense linear algebra,"
|
||||
* in Proc. 2008 ACM/IEEE Conf. on Supercomputing (SC '08),
|
||||
* Piscataway, NJ: IEEE Press, 2008, pp. Art. 31:1-11.
|
||||
*/
|
||||
|
||||
// System includes
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// CUDA runtime
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "nvrtc_helper.h"
|
||||
|
||||
// Helper functions and utilities to work with CUDA
|
||||
#include <helper_functions.h>
|
||||
|
||||
void constantInit(float *data, int size, float val)
|
||||
{
|
||||
for (int i = 0; i < size; ++i) {
|
||||
data[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a simple test of matrix multiplication using CUDA
|
||||
*/
|
||||
int matrixMultiply(int argc, char **argv, int block_size, dim3 &dimsA, dim3 &dimsB)
|
||||
{
|
||||
// Allocate host memory for matrices A and B
|
||||
unsigned int size_A = dimsA.x * dimsA.y;
|
||||
unsigned int mem_size_A = sizeof(float) * size_A;
|
||||
float *h_A = (float *)malloc(mem_size_A);
|
||||
unsigned int size_B = dimsB.x * dimsB.y;
|
||||
unsigned int mem_size_B = sizeof(float) * size_B;
|
||||
float *h_B = (float *)malloc(mem_size_B);
|
||||
|
||||
// Initialize host memory
|
||||
const float valB = 0.01f;
|
||||
constantInit(h_A, size_A, 1.0f);
|
||||
constantInit(h_B, size_B, valB);
|
||||
|
||||
// Allocate device memory
|
||||
CUdeviceptr d_A, d_B, d_C;
|
||||
|
||||
char *cubin, *kernel_file;
|
||||
size_t cubinSize;
|
||||
|
||||
kernel_file = sdkFindFilePath("matrixMul_kernel.cu", argv[0]);
|
||||
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 1);
|
||||
|
||||
CUmodule module = loadCUBIN(cubin, argc, argv);
|
||||
|
||||
// Allocate host matrix C
|
||||
dim3 dimsC(dimsB.x, dimsA.y, 1);
|
||||
unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);
|
||||
float *h_C = (float *)malloc(mem_size_C);
|
||||
|
||||
if (h_C == NULL) {
|
||||
fprintf(stderr, "Failed to allocate host matrix C!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
checkCudaErrors(cuMemAlloc(&d_A, mem_size_A));
|
||||
checkCudaErrors(cuMemAlloc(&d_B, mem_size_B));
|
||||
checkCudaErrors(cuMemAlloc(&d_C, mem_size_C));
|
||||
|
||||
// copy host memory to device
|
||||
checkCudaErrors(cuMemcpyHtoD(d_A, h_A, mem_size_A));
|
||||
checkCudaErrors(cuMemcpyHtoD(d_B, h_B, mem_size_B));
|
||||
|
||||
// Setup execution parameters
|
||||
dim3 threads(block_size, block_size);
|
||||
dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);
|
||||
|
||||
// Create and start timer
|
||||
printf("Computing result using CUDA Kernel...\n");
|
||||
|
||||
CUfunction kernel_addr;
|
||||
if (block_size == 16) {
|
||||
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "matrixMulCUDA_block16"));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "matrixMulCUDA_block32"));
|
||||
}
|
||||
|
||||
void *arr[] = {(void *)&d_C, (void *)&d_A, (void *)&d_B, (void *)&dimsA.x, (void *)&dimsB.x};
|
||||
|
||||
// Execute the kernel
|
||||
int nIter = 300;
|
||||
|
||||
for (int j = 0; j < nIter; j++) {
|
||||
checkCudaErrors(cuLaunchKernel(kernel_addr,
|
||||
grid.x,
|
||||
grid.y,
|
||||
grid.z, /* grid dim */
|
||||
threads.x,
|
||||
threads.y,
|
||||
threads.z, /* block dim */
|
||||
0,
|
||||
0, /* shared mem, stream */
|
||||
&arr[0], /* arguments */
|
||||
0));
|
||||
|
||||
checkCudaErrors(cuCtxSynchronize());
|
||||
}
|
||||
|
||||
// Copy result from device to host
|
||||
checkCudaErrors(cuMemcpyDtoH(h_C, d_C, mem_size_C));
|
||||
|
||||
printf("Checking computed result for correctness: ");
|
||||
|
||||
bool correct = true;
|
||||
|
||||
// test relative error by the formula
|
||||
// |<x, y>_cpu - <x,y>_gpu|/<|x|, |y|> < eps
|
||||
|
||||
double eps = 1.e-6; // machine zero
|
||||
|
||||
for (int i = 0; i < (int)(dimsC.x * dimsC.y); i++) {
|
||||
double abs_err = fabs(h_C[i] - (dimsA.x * valB));
|
||||
double dot_length = dimsA.x;
|
||||
double abs_val = fabs(h_C[i]);
|
||||
double rel_err = abs_err / abs_val / dot_length;
|
||||
|
||||
if (rel_err > eps) {
|
||||
printf("Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\n", i, h_C[i], dimsA.x * valB, eps);
|
||||
correct = false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s\n", correct ? "Result = PASS" : "Result = FAIL");
|
||||
|
||||
printf("\nNOTE: The CUDA Samples are not meant for performance measurements. "
|
||||
"Results may vary when GPU Boost is enabled.\n");
|
||||
|
||||
// Clean up memory
|
||||
free(h_A);
|
||||
free(h_B);
|
||||
free(h_C);
|
||||
|
||||
checkCudaErrors(cuMemFree(d_A));
|
||||
checkCudaErrors(cuMemFree(d_B));
|
||||
checkCudaErrors(cuMemFree(d_C));
|
||||
|
||||
if (correct) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Program main
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("[Matrix Multiply Using CUDA] - Starting...\n");
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "help") || checkCmdLineFlag(argc, (const char **)argv, "?")) {
|
||||
printf("Usage -device=n (n >= 0 for deviceID)\n");
|
||||
printf(" -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\n");
|
||||
printf(" -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\n");
|
||||
printf(" Note: Outer matrix dimensions of A & B matrices must be equal.\n");
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int block_size = 32;
|
||||
|
||||
// original:
|
||||
dim3 dimsA(5 * 2 * block_size, 5 * 2 * block_size, 1);
|
||||
dim3 dimsB(5 * 4 * block_size, 5 * 2 * block_size, 1);
|
||||
|
||||
// reduce sizes to avoid running out of memory
|
||||
// dim3 dimsA(32,32, 1);
|
||||
// dim3 dimsB(32,32,1);
|
||||
|
||||
// width of Matrix A
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "wA")) {
|
||||
dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, "wA");
|
||||
}
|
||||
|
||||
// height of Matrix A
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "hA")) {
|
||||
dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, "hA");
|
||||
}
|
||||
|
||||
// width of Matrix B
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "wB")) {
|
||||
dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, "wB");
|
||||
}
|
||||
|
||||
// height of Matrix B
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "hB")) {
|
||||
dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, "hB");
|
||||
}
|
||||
|
||||
if (dimsA.x != dimsB.y) {
|
||||
printf("Error: outer matrix dimensions must be equal. (%d != %d)\n", dimsA.x, dimsB.y);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("MatrixA(%d,%d), MatrixB(%d,%d)\n", dimsA.x, dimsA.y, dimsB.x, dimsB.y);
|
||||
|
||||
int matrix_result = matrixMultiply(argc, argv, block_size, dimsA, dimsB);
|
||||
|
||||
exit(matrix_result);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Matrix multiplication: C = A * B.
|
||||
* Host code.
|
||||
*
|
||||
* This sample implements matrix multiplication as described in Chapter 3
|
||||
* of the programming guide.
|
||||
* It has been written for clarity of exposition to illustrate various CUDA
|
||||
* programming principles, not with the goal of providing the most
|
||||
* performant generic kernel for matrix multiplication.
|
||||
*
|
||||
* See also:
|
||||
* V. Volkov and J. Demmel, "Benchmarking GPUs to tune dense linear algebra,"
|
||||
* in Proc. 2008 ACM/IEEE Conf. on Supercomputing (SC '08),
|
||||
* Piscataway, NJ: IEEE Press, 2008, pp. Art. 31:1-11.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Matrix multiplication (CUDA Kernel) on the device: C = A * B
|
||||
* wA is A's width and wB is B's width
|
||||
*/
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
template <int BLOCK_SIZE> __device__ void matrixMulCUDA(float *C, float *A, float *B, int wA, int wB)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cooperative_groups::thread_block cta = cooperative_groups::this_thread_block();
|
||||
// Block index
|
||||
int bx = blockIdx.x;
|
||||
int by = blockIdx.y;
|
||||
|
||||
// Thread index
|
||||
int tx = threadIdx.x;
|
||||
int ty = threadIdx.y;
|
||||
|
||||
// Index of the first sub-matrix of A processed by the block
|
||||
int aBegin = wA * BLOCK_SIZE * by;
|
||||
|
||||
// Index of the last sub-matrix of A processed by the block
|
||||
int aEnd = aBegin + wA - 1;
|
||||
|
||||
// Step size used to iterate through the sub-matrices of A
|
||||
int aStep = BLOCK_SIZE;
|
||||
|
||||
// Index of the first sub-matrix of B processed by the block
|
||||
int bBegin = BLOCK_SIZE * bx;
|
||||
|
||||
// Step size used to iterate through the sub-matrices of B
|
||||
int bStep = BLOCK_SIZE * wB;
|
||||
|
||||
// Csub is used to store the element of the block sub-matrix
|
||||
// that is computed by the thread
|
||||
float Csub = 0;
|
||||
|
||||
// Loop over all the sub-matrices of A and B
|
||||
// required to compute the block sub-matrix
|
||||
for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {
|
||||
// Declaration of the shared memory array As used to
|
||||
// store the sub-matrix of A
|
||||
__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
|
||||
|
||||
// Declaration of the shared memory array Bs used to
|
||||
// store the sub-matrix of B
|
||||
__shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
|
||||
|
||||
// Load the matrices from device memory
|
||||
// to shared memory; each thread loads
|
||||
// one element of each matrix
|
||||
As[ty][tx] = A[a + wA * ty + tx];
|
||||
Bs[ty][tx] = B[b + wB * ty + tx];
|
||||
|
||||
// Synchronize to make sure the matrices are loaded
|
||||
cooperative_groups::sync(cta);
|
||||
|
||||
// Multiply the two matrices together;
|
||||
// each thread computes one element
|
||||
// of the block sub-matrix
|
||||
#pragma unroll
|
||||
for (int k = 0; k < BLOCK_SIZE; ++k) {
|
||||
Csub += As[ty][k] * Bs[k][tx];
|
||||
}
|
||||
|
||||
// Synchronize to make sure that the preceding
|
||||
// computation is done before loading two new
|
||||
// sub-matrices of A and B in the next iteration
|
||||
cooperative_groups::sync(cta);
|
||||
}
|
||||
|
||||
// Write the block sub-matrix to device memory;
|
||||
// each thread writes one element
|
||||
int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;
|
||||
C[c + wB * ty + tx] = Csub;
|
||||
}
|
||||
|
||||
extern "C" __global__ void matrixMulCUDA_block16(float *C, float *A, float *B, int wA, int wB)
|
||||
{
|
||||
matrixMulCUDA<16>(C, A, B, wA, wB);
|
||||
}
|
||||
|
||||
extern "C" __global__ void matrixMulCUDA_block32(float *C, float *A, float *B, int wA, int wB)
|
||||
{
|
||||
matrixMulCUDA<32>(C, A, B, wA, wB);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
# mergeSort - Merge Sort
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a merge sort (also known as Batcher's sort), algorithms belonging to the class of sorting networks. While generally subefficient on large sequences compared to algorithms with better asymptotic algorithmic complexity (i.e. merge sort or radix sort), may be the algorithms of choice for sorting batches of short- to mid-sized (key, value) array pairs. Refer to the excellent tutorial by H. W. Lang http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/indexen.htm
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Data-Parallel Algorithms
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMalloc, cudaDeviceSynchronize, cudaMemcpy, cudaFree
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
@@ -1,281 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
#include <assert.h>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
#include "mergeSort_common.h"
|
||||
|
||||
inline __device__ void Comparator(uint &keyA, uint &valA, uint &keyB, uint &valB, uint arrowDir)
|
||||
{
|
||||
uint t;
|
||||
|
||||
if ((keyA > keyB) == arrowDir) {
|
||||
t = keyA;
|
||||
keyA = keyB;
|
||||
keyB = t;
|
||||
t = valA;
|
||||
valA = valB;
|
||||
valB = t;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void
|
||||
bitonicSortSharedKernel(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint sortDir)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
// Shared memory storage for one or more short vectors
|
||||
__shared__ uint s_key[SHARED_SIZE_LIMIT];
|
||||
__shared__ uint s_val[SHARED_SIZE_LIMIT];
|
||||
|
||||
// Offset to the beginning of subbatch and load data
|
||||
d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
s_key[threadIdx.x + 0] = d_SrcKey[0];
|
||||
s_val[threadIdx.x + 0] = d_SrcVal[0];
|
||||
s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];
|
||||
s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];
|
||||
|
||||
for (uint size = 2; size < arrayLength; size <<= 1) {
|
||||
// Bitonic merge
|
||||
uint dir = (threadIdx.x & (size / 2)) != 0;
|
||||
|
||||
for (uint stride = size / 2; stride > 0; stride >>= 1) {
|
||||
cg::sync(cta);
|
||||
uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));
|
||||
Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);
|
||||
}
|
||||
}
|
||||
|
||||
// ddd == sortDir for the last bitonic merge step
|
||||
{
|
||||
for (uint stride = arrayLength / 2; stride > 0; stride >>= 1) {
|
||||
cg::sync(cta);
|
||||
uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));
|
||||
Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], sortDir);
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
d_DstKey[0] = s_key[threadIdx.x + 0];
|
||||
d_DstVal[0] = s_val[threadIdx.x + 0];
|
||||
d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];
|
||||
d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];
|
||||
}
|
||||
|
||||
// Helper function (also used by odd-even merge sort)
|
||||
extern "C" uint factorRadix2(uint *log2L, uint L)
|
||||
{
|
||||
if (!L) {
|
||||
*log2L = 0;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
for (*log2L = 0; (L & 1) == 0; L >>= 1, *log2L++)
|
||||
;
|
||||
|
||||
return L;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void bitonicSortShared(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint batchSize,
|
||||
uint arrayLength,
|
||||
uint sortDir)
|
||||
{
|
||||
// Nothing to sort
|
||||
if (arrayLength < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only power-of-two array lengths are supported by this implementation
|
||||
uint log2L;
|
||||
uint factorizationRemainder = factorRadix2(&log2L, arrayLength);
|
||||
assert(factorizationRemainder == 1);
|
||||
|
||||
uint blockCount = batchSize * arrayLength / SHARED_SIZE_LIMIT;
|
||||
uint threadCount = SHARED_SIZE_LIMIT / 2;
|
||||
|
||||
assert(arrayLength <= SHARED_SIZE_LIMIT);
|
||||
assert((batchSize * arrayLength) % SHARED_SIZE_LIMIT == 0);
|
||||
|
||||
bitonicSortSharedKernel<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, sortDir);
|
||||
getLastCudaError("bitonicSortSharedKernel<<<>>> failed!\n");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 3: merge elementary intervals
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static inline __host__ __device__ uint iDivUp(uint a, uint b) { return ((a % b) == 0) ? (a / b) : (a / b + 1); }
|
||||
|
||||
static inline __host__ __device__ uint getSampleCount(uint dividend) { return iDivUp(dividend, SAMPLE_STRIDE); }
|
||||
|
||||
template <uint sortDir>
|
||||
static inline __device__ void
|
||||
ComparatorExtended(uint &keyA, uint &valA, uint &flagA, uint &keyB, uint &valB, uint &flagB, uint arrowDir)
|
||||
{
|
||||
uint t;
|
||||
|
||||
if ((!(flagA || flagB) && ((keyA > keyB) == arrowDir)) || ((arrowDir == sortDir) && (flagA == 1))
|
||||
|| ((arrowDir != sortDir) && (flagB == 1))) {
|
||||
t = keyA;
|
||||
keyA = keyB;
|
||||
keyB = t;
|
||||
t = valA;
|
||||
valA = valB;
|
||||
valB = t;
|
||||
t = flagA;
|
||||
flagA = flagB;
|
||||
flagB = t;
|
||||
}
|
||||
}
|
||||
|
||||
template <uint sortDir>
|
||||
__global__ void bitonicMergeElementaryIntervalsKernel(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint *d_LimitsA,
|
||||
uint *d_LimitsB,
|
||||
uint stride,
|
||||
uint N)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ uint s_key[2 * SAMPLE_STRIDE];
|
||||
__shared__ uint s_val[2 * SAMPLE_STRIDE];
|
||||
__shared__ uint s_inf[2 * SAMPLE_STRIDE];
|
||||
|
||||
const uint intervalI = blockIdx.x & ((2 * stride) / SAMPLE_STRIDE - 1);
|
||||
const uint segmentBase = (blockIdx.x - intervalI) * SAMPLE_STRIDE;
|
||||
d_SrcKey += segmentBase;
|
||||
d_SrcVal += segmentBase;
|
||||
d_DstKey += segmentBase;
|
||||
d_DstVal += segmentBase;
|
||||
|
||||
// Set up threadblock-wide parameters
|
||||
__shared__ uint startSrcA, lenSrcA, startSrcB, lenSrcB, startDst;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
uint segmentElementsA = stride;
|
||||
uint segmentElementsB = umin(stride, N - segmentBase - stride);
|
||||
uint segmentSamplesA = stride / SAMPLE_STRIDE;
|
||||
uint segmentSamplesB = getSampleCount(segmentElementsB);
|
||||
uint segmentSamples = segmentSamplesA + segmentSamplesB;
|
||||
|
||||
startSrcA = d_LimitsA[blockIdx.x];
|
||||
startSrcB = d_LimitsB[blockIdx.x];
|
||||
startDst = startSrcA + startSrcB;
|
||||
|
||||
uint endSrcA = (intervalI + 1 < segmentSamples) ? d_LimitsA[blockIdx.x + 1] : segmentElementsA;
|
||||
uint endSrcB = (intervalI + 1 < segmentSamples) ? d_LimitsB[blockIdx.x + 1] : segmentElementsB;
|
||||
lenSrcA = endSrcA - startSrcA;
|
||||
lenSrcB = endSrcB - startSrcB;
|
||||
}
|
||||
|
||||
s_inf[threadIdx.x + 0] = 1;
|
||||
s_inf[threadIdx.x + SAMPLE_STRIDE] = 1;
|
||||
|
||||
// Load input data
|
||||
cg::sync(cta);
|
||||
|
||||
if (threadIdx.x < lenSrcA) {
|
||||
s_key[threadIdx.x] = d_SrcKey[0 + startSrcA + threadIdx.x];
|
||||
s_val[threadIdx.x] = d_SrcVal[0 + startSrcA + threadIdx.x];
|
||||
s_inf[threadIdx.x] = 0;
|
||||
}
|
||||
|
||||
// Prepare for bitonic merge by inversing the ordering
|
||||
if (threadIdx.x < lenSrcB) {
|
||||
s_key[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = d_SrcKey[stride + startSrcB + threadIdx.x];
|
||||
s_val[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = d_SrcVal[stride + startSrcB + threadIdx.x];
|
||||
s_inf[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = 0;
|
||||
}
|
||||
|
||||
//"Extended" bitonic merge
|
||||
for (uint stride = SAMPLE_STRIDE; stride > 0; stride >>= 1) {
|
||||
cg::sync(cta);
|
||||
uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));
|
||||
ComparatorExtended<sortDir>(s_key[pos + 0],
|
||||
s_val[pos + 0],
|
||||
s_inf[pos + 0],
|
||||
s_key[pos + stride],
|
||||
s_val[pos + stride],
|
||||
s_inf[pos + stride],
|
||||
sortDir);
|
||||
}
|
||||
|
||||
// Store sorted data
|
||||
cg::sync(cta);
|
||||
d_DstKey += startDst;
|
||||
d_DstVal += startDst;
|
||||
|
||||
if (threadIdx.x < lenSrcA) {
|
||||
d_DstKey[threadIdx.x] = s_key[threadIdx.x];
|
||||
d_DstVal[threadIdx.x] = s_val[threadIdx.x];
|
||||
}
|
||||
|
||||
if (threadIdx.x < lenSrcB) {
|
||||
d_DstKey[lenSrcA + threadIdx.x] = s_key[lenSrcA + threadIdx.x];
|
||||
d_DstVal[lenSrcA + threadIdx.x] = s_val[lenSrcA + threadIdx.x];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void bitonicMergeElementaryIntervals(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint *d_LimitsA,
|
||||
uint *d_LimitsB,
|
||||
uint stride,
|
||||
uint N,
|
||||
uint sortDir)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
|
||||
uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;
|
||||
|
||||
if (sortDir) {
|
||||
bitonicMergeElementaryIntervalsKernel<1U>
|
||||
<<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);
|
||||
getLastCudaError("mergeElementaryIntervalsKernel<1> failed\n");
|
||||
}
|
||||
else {
|
||||
bitonicMergeElementaryIntervalsKernel<0U>
|
||||
<<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);
|
||||
getLastCudaError("mergeElementaryIntervalsKernel<0> failed\n");
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_functions.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mergeSort_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test driver
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
uint *h_SrcKey, *h_SrcVal, *h_DstKey, *h_DstVal;
|
||||
uint *d_SrcKey, *d_SrcVal, *d_BufKey, *d_BufVal, *d_DstKey, *d_DstVal;
|
||||
StopWatchInterface *hTimer = NULL;
|
||||
|
||||
const uint N = 4 * 1048576;
|
||||
const uint DIR = 1;
|
||||
const uint numValues = 65536;
|
||||
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
int dev = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (dev == -1) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Allocating and initializing host arrays...\n\n");
|
||||
sdkCreateTimer(&hTimer);
|
||||
h_SrcKey = (uint *)malloc(N * sizeof(uint));
|
||||
h_SrcVal = (uint *)malloc(N * sizeof(uint));
|
||||
h_DstKey = (uint *)malloc(N * sizeof(uint));
|
||||
h_DstVal = (uint *)malloc(N * sizeof(uint));
|
||||
|
||||
srand(2009);
|
||||
|
||||
for (uint i = 0; i < N; i++) {
|
||||
h_SrcKey[i] = rand() % numValues;
|
||||
}
|
||||
|
||||
fillValues(h_SrcVal, N);
|
||||
|
||||
printf("Allocating and initializing CUDA arrays...\n\n");
|
||||
checkCudaErrors(cudaMalloc((void **)&d_DstKey, N * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_DstVal, N * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_BufKey, N * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_BufVal, N * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_SrcKey, N * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_SrcVal, N * sizeof(uint)));
|
||||
checkCudaErrors(cudaMemcpy(d_SrcKey, h_SrcKey, N * sizeof(uint), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_SrcVal, h_SrcVal, N * sizeof(uint), cudaMemcpyHostToDevice));
|
||||
|
||||
printf("Initializing GPU merge sort...\n");
|
||||
initMergeSort();
|
||||
|
||||
printf("Running GPU merge sort...\n");
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
sdkResetTimer(&hTimer);
|
||||
sdkStartTimer(&hTimer);
|
||||
mergeSort(d_DstKey, d_DstVal, d_BufKey, d_BufVal, d_SrcKey, d_SrcVal, N, DIR);
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
sdkStopTimer(&hTimer);
|
||||
printf("Time: %f ms\n", sdkGetTimerValue(&hTimer));
|
||||
|
||||
printf("Reading back GPU merge sort results...\n");
|
||||
checkCudaErrors(cudaMemcpy(h_DstKey, d_DstKey, N * sizeof(uint), cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaMemcpy(h_DstVal, d_DstVal, N * sizeof(uint), cudaMemcpyDeviceToHost));
|
||||
|
||||
printf("Inspecting the results...\n");
|
||||
uint keysFlag = validateSortedKeys(h_DstKey, h_SrcKey, 1, N, numValues, DIR);
|
||||
|
||||
uint valuesFlag = validateSortedValues(h_DstKey, h_DstVal, h_SrcKey, 1, N);
|
||||
|
||||
printf("Shutting down...\n");
|
||||
closeMergeSort();
|
||||
sdkDeleteTimer(&hTimer);
|
||||
checkCudaErrors(cudaFree(d_SrcVal));
|
||||
checkCudaErrors(cudaFree(d_SrcKey));
|
||||
checkCudaErrors(cudaFree(d_BufVal));
|
||||
checkCudaErrors(cudaFree(d_BufKey));
|
||||
checkCudaErrors(cudaFree(d_DstVal));
|
||||
checkCudaErrors(cudaFree(d_DstKey));
|
||||
free(h_DstVal);
|
||||
free(h_DstKey);
|
||||
free(h_SrcVal);
|
||||
free(h_SrcKey);
|
||||
|
||||
exit((keysFlag && valuesFlag) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on "Designing efficient sorting algorithms for manycore GPUs"
|
||||
* by Nadathur Satish, Mark Harris, and Michael Garland
|
||||
* http://mgarland.org/files/papers/gpusort-ipdps09.pdf
|
||||
*
|
||||
* Victor Podlozhnyuk 09/24/2009
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
#include <helper_cuda.h>
|
||||
|
||||
#include "mergeSort_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static inline __host__ __device__ uint iDivUp(uint a, uint b) { return ((a % b) == 0) ? (a / b) : (a / b + 1); }
|
||||
|
||||
static inline __host__ __device__ uint getSampleCount(uint dividend) { return iDivUp(dividend, SAMPLE_STRIDE); }
|
||||
|
||||
#define W (sizeof(uint) * 8)
|
||||
static inline __device__ uint nextPowerOfTwo(uint x)
|
||||
{
|
||||
/*
|
||||
--x;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
return ++x;
|
||||
*/
|
||||
return 1U << (W - __clz(x - 1));
|
||||
}
|
||||
|
||||
template <uint sortDir> static inline __device__ uint binarySearchInclusive(uint val, uint *data, uint L, uint stride)
|
||||
{
|
||||
if (L == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
|
||||
for (; stride > 0; stride >>= 1) {
|
||||
uint newPos = umin(pos + stride, L);
|
||||
|
||||
if ((sortDir && (data[newPos - 1] <= val)) || (!sortDir && (data[newPos - 1] >= val))) {
|
||||
pos = newPos;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <uint sortDir> static inline __device__ uint binarySearchExclusive(uint val, uint *data, uint L, uint stride)
|
||||
{
|
||||
if (L == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
|
||||
for (; stride > 0; stride >>= 1) {
|
||||
uint newPos = umin(pos + stride, L);
|
||||
|
||||
if ((sortDir && (data[newPos - 1] < val)) || (!sortDir && (data[newPos - 1] > val))) {
|
||||
pos = newPos;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Bottom-level merge sort (binary search-based)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <uint sortDir>
|
||||
__global__ void mergeSortSharedKernel(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ uint s_key[SHARED_SIZE_LIMIT];
|
||||
__shared__ uint s_val[SHARED_SIZE_LIMIT];
|
||||
|
||||
d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;
|
||||
s_key[threadIdx.x + 0] = d_SrcKey[0];
|
||||
s_val[threadIdx.x + 0] = d_SrcVal[0];
|
||||
s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];
|
||||
s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];
|
||||
|
||||
for (uint stride = 1; stride < arrayLength; stride <<= 1) {
|
||||
uint lPos = threadIdx.x & (stride - 1);
|
||||
uint *baseKey = s_key + 2 * (threadIdx.x - lPos);
|
||||
uint *baseVal = s_val + 2 * (threadIdx.x - lPos);
|
||||
|
||||
cg::sync(cta);
|
||||
uint keyA = baseKey[lPos + 0];
|
||||
uint valA = baseVal[lPos + 0];
|
||||
uint keyB = baseKey[lPos + stride];
|
||||
uint valB = baseVal[lPos + stride];
|
||||
uint posA = binarySearchExclusive<sortDir>(keyA, baseKey + stride, stride, stride) + lPos;
|
||||
uint posB = binarySearchInclusive<sortDir>(keyB, baseKey + 0, stride, stride) + lPos;
|
||||
|
||||
cg::sync(cta);
|
||||
baseKey[posA] = keyA;
|
||||
baseVal[posA] = valA;
|
||||
baseKey[posB] = keyB;
|
||||
baseVal[posB] = valB;
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
d_DstKey[0] = s_key[threadIdx.x + 0];
|
||||
d_DstVal[0] = s_val[threadIdx.x + 0];
|
||||
d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];
|
||||
d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];
|
||||
}
|
||||
|
||||
static void mergeSortShared(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint batchSize,
|
||||
uint arrayLength,
|
||||
uint sortDir)
|
||||
{
|
||||
if (arrayLength < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(SHARED_SIZE_LIMIT % arrayLength == 0);
|
||||
assert(((batchSize * arrayLength) % SHARED_SIZE_LIMIT) == 0);
|
||||
uint blockCount = batchSize * arrayLength / SHARED_SIZE_LIMIT;
|
||||
uint threadCount = SHARED_SIZE_LIMIT / 2;
|
||||
|
||||
if (sortDir) {
|
||||
mergeSortSharedKernel<1U><<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength);
|
||||
getLastCudaError("mergeSortShared<1><<<>>> failed\n");
|
||||
}
|
||||
else {
|
||||
mergeSortSharedKernel<0U><<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength);
|
||||
getLastCudaError("mergeSortShared<0><<<>>> failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 1: generate sample ranks
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <uint sortDir>
|
||||
__global__ void
|
||||
generateSampleRanksKernel(uint *d_RanksA, uint *d_RanksB, uint *d_SrcKey, uint stride, uint N, uint threadCount)
|
||||
{
|
||||
uint pos = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (pos >= threadCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint i = pos & ((stride / SAMPLE_STRIDE) - 1);
|
||||
const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);
|
||||
d_SrcKey += segmentBase;
|
||||
d_RanksA += segmentBase / SAMPLE_STRIDE;
|
||||
d_RanksB += segmentBase / SAMPLE_STRIDE;
|
||||
|
||||
const uint segmentElementsA = stride;
|
||||
const uint segmentElementsB = umin(stride, N - segmentBase - stride);
|
||||
const uint segmentSamplesA = getSampleCount(segmentElementsA);
|
||||
const uint segmentSamplesB = getSampleCount(segmentElementsB);
|
||||
|
||||
if (i < segmentSamplesA) {
|
||||
d_RanksA[i] = i * SAMPLE_STRIDE;
|
||||
d_RanksB[i] = binarySearchExclusive<sortDir>(
|
||||
d_SrcKey[i * SAMPLE_STRIDE], d_SrcKey + stride, segmentElementsB, nextPowerOfTwo(segmentElementsB));
|
||||
}
|
||||
|
||||
if (i < segmentSamplesB) {
|
||||
d_RanksB[(stride / SAMPLE_STRIDE) + i] = i * SAMPLE_STRIDE;
|
||||
d_RanksA[(stride / SAMPLE_STRIDE) + i] = binarySearchInclusive<sortDir>(
|
||||
d_SrcKey[stride + i * SAMPLE_STRIDE], d_SrcKey + 0, segmentElementsA, nextPowerOfTwo(segmentElementsA));
|
||||
}
|
||||
}
|
||||
|
||||
static void generateSampleRanks(uint *d_RanksA, uint *d_RanksB, uint *d_SrcKey, uint stride, uint N, uint sortDir)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
uint threadCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)
|
||||
: (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);
|
||||
|
||||
if (sortDir) {
|
||||
generateSampleRanksKernel<1U>
|
||||
<<<iDivUp(threadCount, 256), 256>>>(d_RanksA, d_RanksB, d_SrcKey, stride, N, threadCount);
|
||||
getLastCudaError("generateSampleRanksKernel<1U><<<>>> failed\n");
|
||||
}
|
||||
else {
|
||||
generateSampleRanksKernel<0U>
|
||||
<<<iDivUp(threadCount, 256), 256>>>(d_RanksA, d_RanksB, d_SrcKey, stride, N, threadCount);
|
||||
getLastCudaError("generateSampleRanksKernel<0U><<<>>> failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 2: generate sample ranks and indices
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__global__ void mergeRanksAndIndicesKernel(uint *d_Limits, uint *d_Ranks, uint stride, uint N, uint threadCount)
|
||||
{
|
||||
uint pos = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (pos >= threadCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint i = pos & ((stride / SAMPLE_STRIDE) - 1);
|
||||
const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);
|
||||
d_Ranks += (pos - i) * 2;
|
||||
d_Limits += (pos - i) * 2;
|
||||
|
||||
const uint segmentElementsA = stride;
|
||||
const uint segmentElementsB = umin(stride, N - segmentBase - stride);
|
||||
const uint segmentSamplesA = getSampleCount(segmentElementsA);
|
||||
const uint segmentSamplesB = getSampleCount(segmentElementsB);
|
||||
|
||||
if (i < segmentSamplesA) {
|
||||
uint dstPos = binarySearchExclusive<1U>(
|
||||
d_Ranks[i], d_Ranks + segmentSamplesA, segmentSamplesB, nextPowerOfTwo(segmentSamplesB))
|
||||
+ i;
|
||||
d_Limits[dstPos] = d_Ranks[i];
|
||||
}
|
||||
|
||||
if (i < segmentSamplesB) {
|
||||
uint dstPos = binarySearchInclusive<1U>(
|
||||
d_Ranks[segmentSamplesA + i], d_Ranks, segmentSamplesA, nextPowerOfTwo(segmentSamplesA))
|
||||
+ i;
|
||||
d_Limits[dstPos] = d_Ranks[segmentSamplesA + i];
|
||||
}
|
||||
}
|
||||
|
||||
static void mergeRanksAndIndices(uint *d_LimitsA, uint *d_LimitsB, uint *d_RanksA, uint *d_RanksB, uint stride, uint N)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
uint threadCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)
|
||||
: (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);
|
||||
|
||||
mergeRanksAndIndicesKernel<<<iDivUp(threadCount, 256), 256>>>(d_LimitsA, d_RanksA, stride, N, threadCount);
|
||||
getLastCudaError("mergeRanksAndIndicesKernel(A)<<<>>> failed\n");
|
||||
|
||||
mergeRanksAndIndicesKernel<<<iDivUp(threadCount, 256), 256>>>(d_LimitsB, d_RanksB, stride, N, threadCount);
|
||||
getLastCudaError("mergeRanksAndIndicesKernel(B)<<<>>> failed\n");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 3: merge elementary intervals
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <uint sortDir>
|
||||
inline __device__ void merge(uint *dstKey,
|
||||
uint *dstVal,
|
||||
uint *srcAKey,
|
||||
uint *srcAVal,
|
||||
uint *srcBKey,
|
||||
uint *srcBVal,
|
||||
uint lenA,
|
||||
uint nPowTwoLenA,
|
||||
uint lenB,
|
||||
uint nPowTwoLenB,
|
||||
cg::thread_block cta)
|
||||
{
|
||||
uint keyA, valA, keyB, valB, dstPosA, dstPosB;
|
||||
|
||||
if (threadIdx.x < lenA) {
|
||||
keyA = srcAKey[threadIdx.x];
|
||||
valA = srcAVal[threadIdx.x];
|
||||
dstPosA = binarySearchExclusive<sortDir>(keyA, srcBKey, lenB, nPowTwoLenB) + threadIdx.x;
|
||||
}
|
||||
|
||||
if (threadIdx.x < lenB) {
|
||||
keyB = srcBKey[threadIdx.x];
|
||||
valB = srcBVal[threadIdx.x];
|
||||
dstPosB = binarySearchInclusive<sortDir>(keyB, srcAKey, lenA, nPowTwoLenA) + threadIdx.x;
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
if (threadIdx.x < lenA) {
|
||||
dstKey[dstPosA] = keyA;
|
||||
dstVal[dstPosA] = valA;
|
||||
}
|
||||
|
||||
if (threadIdx.x < lenB) {
|
||||
dstKey[dstPosB] = keyB;
|
||||
dstVal[dstPosB] = valB;
|
||||
}
|
||||
}
|
||||
|
||||
template <uint sortDir>
|
||||
__global__ void mergeElementaryIntervalsKernel(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint *d_LimitsA,
|
||||
uint *d_LimitsB,
|
||||
uint stride,
|
||||
uint N)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ uint s_key[2 * SAMPLE_STRIDE];
|
||||
__shared__ uint s_val[2 * SAMPLE_STRIDE];
|
||||
|
||||
const uint intervalI = blockIdx.x & ((2 * stride) / SAMPLE_STRIDE - 1);
|
||||
const uint segmentBase = (blockIdx.x - intervalI) * SAMPLE_STRIDE;
|
||||
d_SrcKey += segmentBase;
|
||||
d_SrcVal += segmentBase;
|
||||
d_DstKey += segmentBase;
|
||||
d_DstVal += segmentBase;
|
||||
|
||||
// Set up threadblock-wide parameters
|
||||
__shared__ uint startSrcA, startSrcB, lenSrcA, lenSrcB, startDstA, startDstB;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
uint segmentElementsA = stride;
|
||||
uint segmentElementsB = umin(stride, N - segmentBase - stride);
|
||||
uint segmentSamplesA = getSampleCount(segmentElementsA);
|
||||
uint segmentSamplesB = getSampleCount(segmentElementsB);
|
||||
uint segmentSamples = segmentSamplesA + segmentSamplesB;
|
||||
|
||||
startSrcA = d_LimitsA[blockIdx.x];
|
||||
startSrcB = d_LimitsB[blockIdx.x];
|
||||
uint endSrcA = (intervalI + 1 < segmentSamples) ? d_LimitsA[blockIdx.x + 1] : segmentElementsA;
|
||||
uint endSrcB = (intervalI + 1 < segmentSamples) ? d_LimitsB[blockIdx.x + 1] : segmentElementsB;
|
||||
lenSrcA = endSrcA - startSrcA;
|
||||
lenSrcB = endSrcB - startSrcB;
|
||||
startDstA = startSrcA + startSrcB;
|
||||
startDstB = startDstA + lenSrcA;
|
||||
}
|
||||
|
||||
// Load main input data
|
||||
cg::sync(cta);
|
||||
|
||||
if (threadIdx.x < lenSrcA) {
|
||||
s_key[threadIdx.x + 0] = d_SrcKey[0 + startSrcA + threadIdx.x];
|
||||
s_val[threadIdx.x + 0] = d_SrcVal[0 + startSrcA + threadIdx.x];
|
||||
}
|
||||
|
||||
if (threadIdx.x < lenSrcB) {
|
||||
s_key[threadIdx.x + SAMPLE_STRIDE] = d_SrcKey[stride + startSrcB + threadIdx.x];
|
||||
s_val[threadIdx.x + SAMPLE_STRIDE] = d_SrcVal[stride + startSrcB + threadIdx.x];
|
||||
}
|
||||
|
||||
// Merge data in shared memory
|
||||
cg::sync(cta);
|
||||
merge<sortDir>(s_key,
|
||||
s_val,
|
||||
s_key + 0,
|
||||
s_val + 0,
|
||||
s_key + SAMPLE_STRIDE,
|
||||
s_val + SAMPLE_STRIDE,
|
||||
lenSrcA,
|
||||
SAMPLE_STRIDE,
|
||||
lenSrcB,
|
||||
SAMPLE_STRIDE,
|
||||
cta);
|
||||
|
||||
// Store merged data
|
||||
cg::sync(cta);
|
||||
|
||||
if (threadIdx.x < lenSrcA) {
|
||||
d_DstKey[startDstA + threadIdx.x] = s_key[threadIdx.x];
|
||||
d_DstVal[startDstA + threadIdx.x] = s_val[threadIdx.x];
|
||||
}
|
||||
|
||||
if (threadIdx.x < lenSrcB) {
|
||||
d_DstKey[startDstB + threadIdx.x] = s_key[lenSrcA + threadIdx.x];
|
||||
d_DstVal[startDstB + threadIdx.x] = s_val[lenSrcA + threadIdx.x];
|
||||
}
|
||||
}
|
||||
|
||||
static void mergeElementaryIntervals(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint *d_LimitsA,
|
||||
uint *d_LimitsB,
|
||||
uint stride,
|
||||
uint N,
|
||||
uint sortDir)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;
|
||||
|
||||
if (sortDir) {
|
||||
mergeElementaryIntervalsKernel<1U>
|
||||
<<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);
|
||||
getLastCudaError("mergeElementaryIntervalsKernel<1> failed\n");
|
||||
}
|
||||
else {
|
||||
mergeElementaryIntervalsKernel<0U>
|
||||
<<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);
|
||||
getLastCudaError("mergeElementaryIntervalsKernel<0> failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void bitonicSortShared(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint batchSize,
|
||||
uint arrayLength,
|
||||
uint sortDir);
|
||||
|
||||
extern "C" void bitonicMergeElementaryIntervals(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint *d_LimitsA,
|
||||
uint *d_LimitsB,
|
||||
uint stride,
|
||||
uint N,
|
||||
uint sortDir);
|
||||
|
||||
static uint *d_RanksA, *d_RanksB, *d_LimitsA, *d_LimitsB;
|
||||
static const uint MAX_SAMPLE_COUNT = 32768;
|
||||
|
||||
extern "C" void initMergeSort(void)
|
||||
{
|
||||
checkCudaErrors(cudaMalloc((void **)&d_RanksA, MAX_SAMPLE_COUNT * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_RanksB, MAX_SAMPLE_COUNT * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_LimitsA, MAX_SAMPLE_COUNT * sizeof(uint)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_LimitsB, MAX_SAMPLE_COUNT * sizeof(uint)));
|
||||
}
|
||||
|
||||
extern "C" void closeMergeSort(void)
|
||||
{
|
||||
checkCudaErrors(cudaFree(d_RanksA));
|
||||
checkCudaErrors(cudaFree(d_RanksB));
|
||||
checkCudaErrors(cudaFree(d_LimitsB));
|
||||
checkCudaErrors(cudaFree(d_LimitsA));
|
||||
}
|
||||
|
||||
extern "C" void mergeSort(uint *d_DstKey,
|
||||
uint *d_DstVal,
|
||||
uint *d_BufKey,
|
||||
uint *d_BufVal,
|
||||
uint *d_SrcKey,
|
||||
uint *d_SrcVal,
|
||||
uint N,
|
||||
uint sortDir)
|
||||
{
|
||||
uint stageCount = 0;
|
||||
|
||||
for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1, stageCount++)
|
||||
;
|
||||
|
||||
uint *ikey, *ival, *okey, *oval;
|
||||
|
||||
if (stageCount & 1) {
|
||||
ikey = d_BufKey;
|
||||
ival = d_BufVal;
|
||||
okey = d_DstKey;
|
||||
oval = d_DstVal;
|
||||
}
|
||||
else {
|
||||
ikey = d_DstKey;
|
||||
ival = d_DstVal;
|
||||
okey = d_BufKey;
|
||||
oval = d_BufVal;
|
||||
}
|
||||
|
||||
assert(N <= (SAMPLE_STRIDE * MAX_SAMPLE_COUNT));
|
||||
assert(N % SHARED_SIZE_LIMIT == 0);
|
||||
mergeSortShared(ikey, ival, d_SrcKey, d_SrcVal, N / SHARED_SIZE_LIMIT, SHARED_SIZE_LIMIT, sortDir);
|
||||
|
||||
for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1) {
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
|
||||
// Find sample ranks and prepare for limiters merge
|
||||
generateSampleRanks(d_RanksA, d_RanksB, ikey, stride, N, sortDir);
|
||||
|
||||
// Merge ranks and indices
|
||||
mergeRanksAndIndices(d_LimitsA, d_LimitsB, d_RanksA, d_RanksB, stride, N);
|
||||
|
||||
// Merge elementary intervals
|
||||
mergeElementaryIntervals(okey, oval, ikey, ival, d_LimitsA, d_LimitsB, stride, N, sortDir);
|
||||
|
||||
if (lastSegmentElements <= stride) {
|
||||
// Last merge segment consists of a single array which just needs to be
|
||||
// passed through
|
||||
checkCudaErrors(cudaMemcpy(okey + (N - lastSegmentElements),
|
||||
ikey + (N - lastSegmentElements),
|
||||
lastSegmentElements * sizeof(uint),
|
||||
cudaMemcpyDeviceToDevice));
|
||||
checkCudaErrors(cudaMemcpy(oval + (N - lastSegmentElements),
|
||||
ival + (N - lastSegmentElements),
|
||||
lastSegmentElements * sizeof(uint),
|
||||
cudaMemcpyDeviceToDevice));
|
||||
}
|
||||
|
||||
uint *t;
|
||||
t = ikey;
|
||||
ikey = okey;
|
||||
okey = t;
|
||||
t = ival;
|
||||
ival = oval;
|
||||
oval = t;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Shortcut definitions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define SHARED_SIZE_LIMIT 1024U
|
||||
#define SAMPLE_STRIDE 128
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Extensive sort validation routine
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" uint
|
||||
validateSortedKeys(uint *resKey, uint *srcKey, uint batchSize, uint arrayLength, uint numValues, uint sortDir);
|
||||
|
||||
extern "C" void fillValues(uint *val, uint N);
|
||||
|
||||
extern "C" int validateSortedValues(uint *resKey, uint *resVal, uint *srcKey, uint batchSize, uint arrayLength);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// CUDA merge sort
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" void initMergeSort(void);
|
||||
|
||||
extern "C" void closeMergeSort(void);
|
||||
|
||||
extern "C" void
|
||||
mergeSort(uint *dstKey, uint *dstVal, uint *bufKey, uint *bufVal, uint *srcKey, uint *srcVal, uint N, uint sortDir);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// CPU "emulation"
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" void
|
||||
mergeSortHost(uint *dstKey, uint *dstVal, uint *bufKey, uint *bufVal, uint *srcKey, uint *srcVal, uint N, uint sortDir);
|
||||
@@ -1,363 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mergeSort_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void checkOrder(uint *data, uint N, uint sortDir)
|
||||
{
|
||||
if (N <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < N - 1; i++)
|
||||
if ((sortDir && (data[i] > data[i + 1])) || (!sortDir && (data[i] < data[i + 1]))) {
|
||||
fprintf(stderr, "checkOrder() failed!!!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static uint umin(uint a, uint b) { return (a <= b) ? a : b; }
|
||||
|
||||
static uint getSampleCount(uint dividend)
|
||||
{
|
||||
return ((dividend % SAMPLE_STRIDE) != 0) ? (dividend / SAMPLE_STRIDE + 1) : (dividend / SAMPLE_STRIDE);
|
||||
}
|
||||
|
||||
static uint nextPowerOfTwo(uint x)
|
||||
{
|
||||
--x;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
return ++x;
|
||||
}
|
||||
|
||||
static uint binarySearchInclusive(uint val, uint *data, uint L, uint sortDir)
|
||||
{
|
||||
if (L == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
|
||||
for (uint stride = nextPowerOfTwo(L); stride > 0; stride >>= 1) {
|
||||
uint newPos = umin(pos + stride, L);
|
||||
|
||||
if ((sortDir && (data[newPos - 1] <= val)) || (!sortDir && (data[newPos - 1] >= val))) {
|
||||
pos = newPos;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static uint binarySearchExclusive(uint val, uint *data, uint L, uint sortDir)
|
||||
{
|
||||
if (L == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
|
||||
for (uint stride = nextPowerOfTwo(L); stride > 0; stride >>= 1) {
|
||||
uint newPos = umin(pos + stride, L);
|
||||
|
||||
if ((sortDir && (data[newPos - 1] < val)) || (!sortDir && (data[newPos - 1] > val))) {
|
||||
pos = newPos;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 1: find sample ranks in each segment
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void generateSampleRanks(uint *ranksA, uint *ranksB, uint *srcKey, uint stride, uint N, uint sortDir)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
uint sampleCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)
|
||||
: (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);
|
||||
|
||||
for (uint pos = 0; pos < sampleCount; pos++) {
|
||||
const uint i = pos & ((stride / SAMPLE_STRIDE) - 1);
|
||||
const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);
|
||||
|
||||
const uint lenA = stride;
|
||||
const uint lenB = umin(stride, N - segmentBase - stride);
|
||||
const uint nA = stride / SAMPLE_STRIDE;
|
||||
const uint nB = getSampleCount(lenB);
|
||||
|
||||
if (i < nA) {
|
||||
ranksA[(segmentBase + 0) / SAMPLE_STRIDE + i] = i * SAMPLE_STRIDE;
|
||||
ranksB[(segmentBase + 0) / SAMPLE_STRIDE + i] = binarySearchExclusive(
|
||||
srcKey[segmentBase + i * SAMPLE_STRIDE], srcKey + segmentBase + stride, lenB, sortDir);
|
||||
}
|
||||
|
||||
if (i < nB) {
|
||||
ranksB[(segmentBase + stride) / SAMPLE_STRIDE + i] = i * SAMPLE_STRIDE;
|
||||
ranksA[(segmentBase + stride) / SAMPLE_STRIDE + i] = binarySearchInclusive(
|
||||
srcKey[segmentBase + stride + i * SAMPLE_STRIDE], srcKey + segmentBase, lenA, sortDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 2: merge ranks and indices to derive elementary intervals
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void mergeRanksAndIndices(uint *limits, uint *ranks, uint stride, uint N)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
uint sampleCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)
|
||||
: (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);
|
||||
|
||||
for (uint pos = 0; pos < sampleCount; pos++) {
|
||||
const uint i = pos & ((stride / SAMPLE_STRIDE) - 1);
|
||||
const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);
|
||||
|
||||
const uint lenA = stride;
|
||||
const uint lenB = umin(stride, N - segmentBase - stride);
|
||||
const uint nA = stride / SAMPLE_STRIDE;
|
||||
const uint nB = getSampleCount(lenB);
|
||||
|
||||
if (i < nA) {
|
||||
uint dstPosA =
|
||||
binarySearchExclusive(
|
||||
ranks[(segmentBase + 0) / SAMPLE_STRIDE + i], ranks + (segmentBase + stride) / SAMPLE_STRIDE, nB, 1)
|
||||
+ i;
|
||||
assert(dstPosA < nA + nB);
|
||||
limits[(segmentBase / SAMPLE_STRIDE) + dstPosA] = ranks[(segmentBase + 0) / SAMPLE_STRIDE + i];
|
||||
}
|
||||
|
||||
if (i < nB) {
|
||||
uint dstPosA =
|
||||
binarySearchInclusive(
|
||||
ranks[(segmentBase + stride) / SAMPLE_STRIDE + i], ranks + (segmentBase + 0) / SAMPLE_STRIDE, nA, 1)
|
||||
+ i;
|
||||
assert(dstPosA < nA + nB);
|
||||
limits[(segmentBase / SAMPLE_STRIDE) + dstPosA] = ranks[(segmentBase + stride) / SAMPLE_STRIDE + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Merge step 3: merge elementary intervals (each interval is <= SAMPLE_STRIDE)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void merge(uint *dstKey,
|
||||
uint *dstVal,
|
||||
uint *srcAKey,
|
||||
uint *srcAVal,
|
||||
uint *srcBKey,
|
||||
uint *srcBVal,
|
||||
uint lenA,
|
||||
uint lenB,
|
||||
uint sortDir)
|
||||
{
|
||||
checkOrder(srcAKey, lenA, sortDir);
|
||||
checkOrder(srcBKey, lenB, sortDir);
|
||||
|
||||
for (uint i = 0; i < lenA; i++) {
|
||||
uint dstPos = binarySearchExclusive(srcAKey[i], srcBKey, lenB, sortDir) + i;
|
||||
assert(dstPos < lenA + lenB);
|
||||
dstKey[dstPos] = srcAKey[i];
|
||||
dstVal[dstPos] = srcAVal[i];
|
||||
}
|
||||
|
||||
for (uint i = 0; i < lenB; i++) {
|
||||
uint dstPos = binarySearchInclusive(srcBKey[i], srcAKey, lenA, sortDir) + i;
|
||||
assert(dstPos < lenA + lenB);
|
||||
dstKey[dstPos] = srcBKey[i];
|
||||
dstVal[dstPos] = srcBVal[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void mergeElementaryIntervals(uint *dstKey,
|
||||
uint *dstVal,
|
||||
uint *srcKey,
|
||||
uint *srcVal,
|
||||
uint *limitsA,
|
||||
uint *limitsB,
|
||||
uint stride,
|
||||
uint N,
|
||||
uint sortDir)
|
||||
{
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;
|
||||
|
||||
for (uint pos = 0; pos < mergePairs; pos++) {
|
||||
uint i = pos & ((2 * stride) / SAMPLE_STRIDE - 1);
|
||||
uint segmentBase = (pos - i) * SAMPLE_STRIDE;
|
||||
|
||||
const uint lenA = stride;
|
||||
const uint lenB = umin(stride, N - segmentBase - stride);
|
||||
const uint nA = stride / SAMPLE_STRIDE;
|
||||
const uint nB = getSampleCount(lenB);
|
||||
const uint n = nA + nB;
|
||||
|
||||
const uint startPosA = limitsA[pos];
|
||||
const uint endPosA = (i + 1 < n) ? limitsA[pos + 1] : lenA;
|
||||
const uint startPosB = limitsB[pos];
|
||||
const uint endPosB = (i + 1 < n) ? limitsB[pos + 1] : lenB;
|
||||
const uint startPosDst = startPosA + startPosB;
|
||||
|
||||
assert(startPosA <= endPosA && endPosA <= lenA);
|
||||
assert(startPosB <= endPosB && endPosB <= lenB);
|
||||
assert((endPosA - startPosA) <= SAMPLE_STRIDE);
|
||||
assert((endPosB - startPosB) <= SAMPLE_STRIDE);
|
||||
|
||||
merge(dstKey + segmentBase + startPosDst,
|
||||
dstVal + segmentBase + startPosDst,
|
||||
(srcKey + segmentBase + 0) + startPosA,
|
||||
(srcVal + segmentBase + 0) + startPosA,
|
||||
(srcKey + segmentBase + stride) + startPosB,
|
||||
(srcVal + segmentBase + stride) + startPosB,
|
||||
endPosA - startPosA,
|
||||
endPosB - startPosB,
|
||||
sortDir);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Retarded bubble sort
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void bubbleSort(uint *key, uint *val, uint N, uint sortDir)
|
||||
{
|
||||
if (N <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint bottom = 0; bottom < N - 1; bottom++) {
|
||||
uint savePos = bottom;
|
||||
uint saveKey = key[bottom];
|
||||
|
||||
for (uint i = bottom + 1; i < N; i++)
|
||||
if ((sortDir && (key[i] < saveKey)) || (!sortDir && (key[i] > saveKey))) {
|
||||
savePos = i;
|
||||
saveKey = key[i];
|
||||
}
|
||||
|
||||
if (savePos != bottom) {
|
||||
uint t;
|
||||
t = key[savePos];
|
||||
key[savePos] = key[bottom];
|
||||
key[bottom] = t;
|
||||
t = val[savePos];
|
||||
val[savePos] = val[bottom];
|
||||
val[bottom] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface function
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" void
|
||||
mergeSortHost(uint *dstKey, uint *dstVal, uint *bufKey, uint *bufVal, uint *srcKey, uint *srcVal, uint N, uint sortDir)
|
||||
{
|
||||
uint *ikey, *ival, *okey, *oval;
|
||||
uint stageCount = 0;
|
||||
|
||||
for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1, stageCount++)
|
||||
;
|
||||
|
||||
if (stageCount & 1) {
|
||||
ikey = bufKey;
|
||||
ival = bufVal;
|
||||
okey = dstKey;
|
||||
oval = dstVal;
|
||||
}
|
||||
else {
|
||||
ikey = dstKey;
|
||||
ival = dstVal;
|
||||
okey = bufKey;
|
||||
oval = bufVal;
|
||||
}
|
||||
|
||||
printf("Bottom-level sort...\n");
|
||||
memcpy(ikey, srcKey, N * sizeof(uint));
|
||||
memcpy(ival, srcVal, N * sizeof(uint));
|
||||
|
||||
for (uint pos = 0; pos < N; pos += SHARED_SIZE_LIMIT) {
|
||||
bubbleSort(ikey + pos, ival + pos, umin(SHARED_SIZE_LIMIT, N - pos), sortDir);
|
||||
}
|
||||
|
||||
printf("Merge...\n");
|
||||
uint *ranksA = (uint *)malloc(getSampleCount(N) * sizeof(uint));
|
||||
uint *ranksB = (uint *)malloc(getSampleCount(N) * sizeof(uint));
|
||||
uint *limitsA = (uint *)malloc(getSampleCount(N) * sizeof(uint));
|
||||
uint *limitsB = (uint *)malloc(getSampleCount(N) * sizeof(uint));
|
||||
memset(ranksA, 0xFF, getSampleCount(N) * sizeof(uint));
|
||||
memset(ranksB, 0xFF, getSampleCount(N) * sizeof(uint));
|
||||
memset(limitsA, 0xFF, getSampleCount(N) * sizeof(uint));
|
||||
memset(limitsB, 0xFF, getSampleCount(N) * sizeof(uint));
|
||||
|
||||
for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1) {
|
||||
uint lastSegmentElements = N % (2 * stride);
|
||||
|
||||
// Find sample ranks and prepare for limiters merge
|
||||
generateSampleRanks(ranksA, ranksB, ikey, stride, N, sortDir);
|
||||
|
||||
// Merge ranks and indices
|
||||
mergeRanksAndIndices(limitsA, ranksA, stride, N);
|
||||
mergeRanksAndIndices(limitsB, ranksB, stride, N);
|
||||
|
||||
// Merge elementary intervals
|
||||
mergeElementaryIntervals(okey, oval, ikey, ival, limitsA, limitsB, stride, N, sortDir);
|
||||
|
||||
if (lastSegmentElements <= stride) {
|
||||
// Last merge segment consists of a single array which just needs to be
|
||||
// passed through
|
||||
memcpy(
|
||||
okey + (N - lastSegmentElements), ikey + (N - lastSegmentElements), lastSegmentElements * sizeof(uint));
|
||||
memcpy(
|
||||
oval + (N - lastSegmentElements), ival + (N - lastSegmentElements), lastSegmentElements * sizeof(uint));
|
||||
}
|
||||
|
||||
uint *t;
|
||||
t = ikey;
|
||||
ikey = okey;
|
||||
okey = t;
|
||||
t = ival;
|
||||
ival = oval;
|
||||
oval = t;
|
||||
}
|
||||
|
||||
free(limitsB);
|
||||
free(limitsA);
|
||||
free(ranksB);
|
||||
free(ranksA);
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mergeSort_common.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Validate sorted keys array (check for integrity and proper order)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" uint
|
||||
validateSortedKeys(uint *resKey, uint *srcKey, uint batchSize, uint arrayLength, uint numValues, uint sortDir)
|
||||
{
|
||||
uint *srcHist;
|
||||
uint *resHist;
|
||||
|
||||
if (arrayLength < 2) {
|
||||
printf("validateSortedKeys(): arrays too short, exiting...\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("...inspecting keys array: ");
|
||||
srcHist = (uint *)malloc(numValues * sizeof(uint));
|
||||
resHist = (uint *)malloc(numValues * sizeof(uint));
|
||||
|
||||
int flag = 1;
|
||||
|
||||
for (uint j = 0; j < batchSize; j++, srcKey += arrayLength, resKey += arrayLength) {
|
||||
// Build histograms for keys arrays
|
||||
memset(srcHist, 0, numValues * sizeof(uint));
|
||||
memset(resHist, 0, numValues * sizeof(uint));
|
||||
|
||||
for (uint i = 0; i < arrayLength; i++) {
|
||||
if ((srcKey[i] < numValues) && (resKey[i] < numValues)) {
|
||||
srcHist[srcKey[i]]++;
|
||||
resHist[resKey[i]]++;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "***Set %u source/result key arrays are not limited properly***\n", j);
|
||||
flag = 0;
|
||||
goto brk;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the histograms
|
||||
for (uint i = 0; i < numValues; i++)
|
||||
if (srcHist[i] != resHist[i]) {
|
||||
fprintf(stderr, "***Set %u source/result keys histograms do not match***\n", j);
|
||||
flag = 0;
|
||||
goto brk;
|
||||
}
|
||||
|
||||
// Finally check the ordering
|
||||
for (uint i = 0; i < arrayLength - 1; i++)
|
||||
if ((sortDir && (resKey[i] > resKey[i + 1])) || (!sortDir && (resKey[i] < resKey[i + 1]))) {
|
||||
fprintf(stderr, "***Set %u result key array is not ordered properly***\n", j);
|
||||
flag = 0;
|
||||
goto brk;
|
||||
}
|
||||
}
|
||||
|
||||
brk:
|
||||
free(resHist);
|
||||
free(srcHist);
|
||||
|
||||
if (flag)
|
||||
printf("OK\n");
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Value validation / stability check routines
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" void fillValues(uint *val, uint N)
|
||||
{
|
||||
for (uint i = 0; i < N; i++)
|
||||
val[i] = i;
|
||||
}
|
||||
|
||||
extern "C" int validateSortedValues(uint *resKey, uint *resVal, uint *srcKey, uint batchSize, uint arrayLength)
|
||||
{
|
||||
int correctFlag = 1, stableFlag = 1;
|
||||
|
||||
printf("...inspecting keys and values array: ");
|
||||
|
||||
for (uint i = 0; i < batchSize; i++, resKey += arrayLength, resVal += arrayLength) {
|
||||
for (uint j = 0; j < arrayLength; j++) {
|
||||
if (resKey[j] != srcKey[resVal[j]])
|
||||
correctFlag = 0;
|
||||
|
||||
if ((j < arrayLength - 1) && (resKey[j] == resKey[j + 1]) && (resVal[j] > resVal[j + 1]))
|
||||
stableFlag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf(correctFlag ? "OK\n" : "***corrupted!!!***\n");
|
||||
printf(stableFlag ? "...stability property: stable!\n" : "...stability property: NOT stable\n");
|
||||
|
||||
return correctFlag;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,23 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleAWBarrier LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# This sample is not supported on QNX
|
||||
@@ -26,16 +21,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Source file
|
||||
# Add target for simpleAWBarrier
|
||||
add_executable(simpleAWBarrier simpleAWBarrier.cu)
|
||||
|
||||
target_compile_options(simpleAWBarrier PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleAWBarrier PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleAWBarrier PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,37 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleAssert LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Removes -DNDEBUG For Print specific logs in this sample.
|
||||
string(REPLACE "-DNDEBUG" "" CMAKE_CUDA_FLAGS_RELEASE "${CMAKE_CUDA_FLAGS_RELEASE}")
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for simpleAssert
|
||||
add_executable(simpleAssert simpleAssert.cu)
|
||||
|
||||
target_compile_options(simpleAssert PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleAssert PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleAssert PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,32 +1,83 @@
|
||||
# simpleAssert - simpleAssert
|
||||
# Sample: Device-Side assert (simpleAssert)
|
||||
|
||||
## Description
|
||||
|
||||
This CUDA Runtime API sample is a very basic sample that implements how to use the assert function in the device code. Requires Compute Capability 2.0 .
|
||||
Use the standard C `assert` macro **inside a CUDA kernel**. The sample launches threads that each assert `gtid < N`; threads whose global index reaches `N` trip the assertion, print a diagnostic message to the host, and cause the kernel to fail. The host detects this through the `cudaErrorAssert` status returned by `cudaDeviceSynchronize` and reports it as the expected outcome.
|
||||
|
||||
This sample demonstrates device-side `assert`, a debugging aid for catching invalid conditions in kernel code, and shows how an assertion failure surfaces on the host as an asynchronous CUDA error.
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
- Calling `assert` directly from device code (`__global__` kernel)
|
||||
- How a failed device assertion is reported to the host as `cudaErrorAssert`
|
||||
- Detecting that error after the launch via `cudaDeviceSynchronize`
|
||||
- Computing a global thread index from `blockIdx`, `blockDim`, and `threadIdx`
|
||||
- Turning an error code into a human-readable message with `cudaGetErrorString`
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Assert
|
||||
- **Device-side `assert`** — `assert(condition)` in a kernel; a false condition halts the kernel, prints `file:line: function: block: ... Assertion ... failed`, and flags the launch as failed
|
||||
- **Asynchronous error reporting** — the assertion failure is not seen at launch time; it is surfaced at the next synchronization point as `cudaErrorAssert`
|
||||
- **Global thread indexing** — `blockIdx.x * blockDim.x + threadIdx.x`
|
||||
|
||||
## Supported SM Architectures
|
||||
## Key APIs
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
### CUDA Runtime
|
||||
- `cudaSetDevice` — select the active GPU
|
||||
- `cudaDeviceGetAttribute` — query compute capability (major, minor) and SM count
|
||||
- `cudaDeviceSynchronize` — block the host until the kernel finishes; flushes assert output and returns `cudaErrorAssert` if an assertion failed
|
||||
- `cudaGetErrorString` — convert a `cudaError_t` into a readable description
|
||||
|
||||
## Supported OSes
|
||||
## Requirements
|
||||
|
||||
Linux, Windows
|
||||
### Hardware
|
||||
- NVIDIA GPU with Compute Capability 7.5 or higher
|
||||
|
||||
## Supported CPU Architecture
|
||||
### Software
|
||||
- CMake 3.20 or newer
|
||||
- A C++17-capable host compiler
|
||||
|
||||
x86_64, armv7l
|
||||
## How to Build
|
||||
|
||||
## CUDA APIs involved
|
||||
See the [top-level README](../../../README.md#building-cuda-samples) for full build instructions, including how to build all samples or a single sample standalone.
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaDeviceSynchronize, cudaGetErrorString
|
||||
## How to Run
|
||||
|
||||
## Prerequisites
|
||||
```bash
|
||||
./simpleAssert
|
||||
```
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
## Expected Output
|
||||
|
||||
## References (for more details)
|
||||
The kernel launches 2 blocks × 32 threads = 64 threads and asserts `gtid < 60`, so the 4 threads with global indices 60–63 fail the assertion. The assertion failures are **expected** — the sample reports `OK` because it successfully detected `cudaErrorAssert`:
|
||||
|
||||
```text
|
||||
simpleAssert starting...
|
||||
|
||||
GPU Device 0: with compute capability X.Y and Number of SMs <smCount>
|
||||
|
||||
Launch kernel to generate assertion failures
|
||||
|
||||
-- Begin assert output
|
||||
|
||||
simpleAssert.cu:50: void simpleAssertKernel(int): block: [1,0,0], thread: [28,0,0] Assertion `gtid < N` failed.
|
||||
...
|
||||
|
||||
-- End assert output
|
||||
|
||||
Device assert failed as expected, CUDA error message is: device-side assert triggered
|
||||
|
||||
simpleAssert completed, returned OK
|
||||
```
|
||||
|
||||
The order and exact set of failing-thread lines may vary between runs.
|
||||
|
||||
## Files
|
||||
|
||||
- `simpleAssert.cu` — device-side `assert` kernel + host driver
|
||||
- `README.md` — this file
|
||||
- `CMakeLists.txt` — build configuration
|
||||
|
||||
## See Also
|
||||
|
||||
- [CUDA C++ Programming Guide - Assertion](https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/cpp-language-extensions.html#assertion)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -25,86 +25,47 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
// Includes, system
|
||||
// System includes
|
||||
#include <cassert>
|
||||
#include <stdio.h>
|
||||
|
||||
// Includes CUDA
|
||||
// CUDA runtime
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
// Utilities and timing functions
|
||||
#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h
|
||||
|
||||
// CUDA helper functions
|
||||
#include <helper_cuda.h> // helper functions for CUDA error check
|
||||
|
||||
const char *sampleName = "simpleAssert";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Auto-Verification Code
|
||||
bool testResult = true;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernels
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Tests assert function.
|
||||
//! Thread whose id > N will print assertion failed error message.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__global__ void testKernel(int N)
|
||||
// Each thread computes its global index and asserts it is below N. Threads with
|
||||
// gtid >= N trip the assertion, which aborts the kernel and surfaces on the host
|
||||
// as cudaErrorAssert at the next synchronization point.
|
||||
__global__ void simpleAssertKernel(int N)
|
||||
{
|
||||
int gtid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
assert(gtid < N);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Declaration, forward
|
||||
void runTest(int argc, char **argv);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Program main
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("%s starting...\n", sampleName);
|
||||
|
||||
runTest(argc, argv);
|
||||
|
||||
printf("%s completed, returned %s\n", sampleName, testResult ? "OK" : "ERROR!");
|
||||
exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void runTest(int argc, char **argv)
|
||||
{
|
||||
int Nblocks = 2;
|
||||
int Nthreads = 32;
|
||||
cudaError_t error;
|
||||
|
||||
#ifndef _WIN32
|
||||
utsname OS_System_Type;
|
||||
uname(&OS_System_Type);
|
||||
printf("%s starting...\n\n", sampleName);
|
||||
|
||||
printf("OS_System_Type.release = %s\n", OS_System_Type.release);
|
||||
// Select device 0 as the active GPU
|
||||
int devID = 0;
|
||||
cudaSetDevice(devID);
|
||||
|
||||
if (!strcasecmp(OS_System_Type.sysname, "Darwin")) {
|
||||
printf("simpleAssert is not current supported on Mac OSX\n\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else {
|
||||
printf("OS Info: <%s>\n\n", OS_System_Type.version);
|
||||
}
|
||||
// Query compute capability (major.minor) and number of SMs on the device
|
||||
int major = 0, minor = 0, smCount = 0;
|
||||
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devID);
|
||||
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, devID);
|
||||
cudaDeviceGetAttribute(&smCount, cudaDevAttrMultiProcessorCount, devID);
|
||||
|
||||
#endif
|
||||
|
||||
// This will pick the best possible CUDA capable device
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
// Print device info
|
||||
printf("GPU Device %d: with compute capability %d.%d and Number of SMs %d\n\n", devID, major, minor, smCount);
|
||||
|
||||
// Kernel configuration, where a one-dimensional
|
||||
// grid and one-dimensional blocks are configured.
|
||||
@@ -112,7 +73,7 @@ void runTest(int argc, char **argv)
|
||||
dim3 dimBlock(Nthreads);
|
||||
|
||||
printf("Launch kernel to generate assertion failures\n");
|
||||
testKernel<<<dimGrid, dimBlock>>>(60);
|
||||
simpleAssertKernel<<<dimGrid, dimBlock>>>(60);
|
||||
|
||||
// Synchronize (flushes assert output).
|
||||
printf("\n-- Begin assert output\n\n");
|
||||
@@ -127,4 +88,7 @@ void runTest(int argc, char **argv)
|
||||
}
|
||||
|
||||
testResult = error == cudaErrorAssert;
|
||||
|
||||
printf("%s completed, returned %s\n", sampleName, testResult ? "OK" : "ERROR!");
|
||||
exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(simpleAssert_nvrtc LANGUAGES C CXX CUDA)
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add sample target executable
|
||||
add_executable(simpleAssert_nvrtc simpleAssert.cpp)
|
||||
|
||||
target_compile_options(simpleAssert_nvrtc PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleAssert_nvrtc PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
target_link_libraries(simpleAssert_nvrtc PRIVATE
|
||||
CUDA::nvrtc
|
||||
CUDA::cuda_driver
|
||||
)
|
||||
|
||||
# Copy clock_kernel.cu to the output directory
|
||||
add_custom_command(TARGET simpleAssert_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/simpleAssert_kernel.cu ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
@@ -1,36 +0,0 @@
|
||||
# simpleAssert_nvrtc - simpleAssert with libNVRTC
|
||||
|
||||
## Description
|
||||
|
||||
This CUDA Runtime API sample is a very basic sample that implements how to use the assert function in the device code. Requires Compute Capability 2.0 .
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Assert, Runtime Compilation
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows, QNX
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuModuleGetFunction, cuLaunchKernel, cuCtxSynchronize
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[NVRTC](../../../README.md#nvrtc)
|
||||
|
||||
## 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)
|
||||
@@ -1,121 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
// Includes, system
|
||||
#include <cassert>
|
||||
#include <stdio.h>
|
||||
|
||||
// Includes CUDA
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "nvrtc_helper.h"
|
||||
|
||||
// Utilities and timing functions
|
||||
#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h
|
||||
|
||||
const char *sampleName = "simpleAssert_nvrtc";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Auto-Verification Code
|
||||
bool testResult = true;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Declaration, forward
|
||||
void runTest(int argc, char **argv);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Program main
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("%s starting...\n", sampleName);
|
||||
|
||||
runTest(argc, argv);
|
||||
|
||||
exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void runTest(int argc, char **argv)
|
||||
{
|
||||
int Nblocks = 2;
|
||||
int Nthreads = 32;
|
||||
|
||||
// Kernel configuration, where a one-dimensional
|
||||
// grid and one-dimensional blocks are configured.
|
||||
|
||||
dim3 dimGrid(Nblocks);
|
||||
dim3 dimBlock(Nthreads);
|
||||
|
||||
printf("Launch kernel to generate assertion failures\n");
|
||||
char *cubin, *kernel_file;
|
||||
size_t cubinSize;
|
||||
|
||||
kernel_file = sdkFindFilePath("simpleAssert_kernel.cu", argv[0]);
|
||||
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
|
||||
|
||||
CUmodule module = loadCUBIN(cubin, argc, argv);
|
||||
CUfunction kernel_addr;
|
||||
|
||||
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "testKernel"));
|
||||
|
||||
int count = 60;
|
||||
void *args[] = {(void *)&count};
|
||||
|
||||
checkCudaErrors(cuLaunchKernel(kernel_addr,
|
||||
dimGrid.x,
|
||||
dimGrid.y,
|
||||
dimGrid.z, /* grid dim */
|
||||
dimBlock.x,
|
||||
dimBlock.y,
|
||||
dimBlock.z, /* block dim */
|
||||
0,
|
||||
0, /* shared mem, stream */
|
||||
&args[0], /* arguments */
|
||||
0));
|
||||
|
||||
// Synchronize (flushes assert output).
|
||||
printf("\n-- Begin assert output\n\n");
|
||||
CUresult res = cuCtxSynchronize();
|
||||
|
||||
printf("\n-- End assert output\n\n");
|
||||
|
||||
// Check for errors and failed asserts in asynchronous kernel launch.
|
||||
if (res == CUDA_ERROR_ASSERT) {
|
||||
printf("Device assert failed as expected\n");
|
||||
}
|
||||
|
||||
testResult = res == CUDA_ERROR_ASSERT;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernels
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Tests assert function.
|
||||
//! Thread whose id > N will print assertion failed error message.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern "C" __global__ void testKernel(int N)
|
||||
{
|
||||
int gtid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
assert(gtid < N);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,35 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleAtomicIntrinsics LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for simpleAtomicIntrinsics
|
||||
add_executable(simpleAtomicIntrinsics simpleAtomicIntrinsics.cu simpleAtomicIntrinsics_cpu.cpp)
|
||||
add_executable(simpleAtomicIntrinsics simpleAtomicIntrinsics.cu)
|
||||
|
||||
target_compile_options(simpleAtomicIntrinsics PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleAtomicIntrinsics PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleAtomicIntrinsics PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,32 +1,123 @@
|
||||
# simpleAtomicIntrinsics - Simple Atomic Intrinsics
|
||||
# simpleAtomicIntrinsics - Atomic vs. Non-Atomic Operations
|
||||
|
||||
## Description
|
||||
|
||||
A simple demonstration of global memory atomic instructions.
|
||||
A CUDA sample that demonstrates **why atomic operations are needed** by running the same work with and without atomics under heavy contention. 1,000,000 threads write into an array of just 10 integers, so roughly 100,000 threads collide on every element. Three operations — add, max, and compare-and-swap — are each shown four ways: a non-atomic kernel, the CUDA atomic intrinsic, `cuda::std::atomic_ref` from CCCL (the `std::atomic` API in device code), and `cuda::atomic_ref` from CCCL (the CUDA-specific variant that natively supports thread scopes):
|
||||
|
||||
Both CCCL variants use `atomic_ref` rather than `atomic` so they act on the *existing* plain-`int` array — this matches the behavior of the intrinsic atomics, which also adds atomic access to memory that already exists. `atomic<int>` would instead require the array elements themselves to be declared separately as the atomic type.
|
||||
|
||||
| Operation | Non-atomic | Intrinsic | cuda::std::atomic_ref | cuda::atomic_ref |
|
||||
|---|---|---|---|---|
|
||||
| Add 1 to an element | `increment` | `increment_atomic` | `increment_atomic_std` | `increment_atomic_cuda` |
|
||||
| Keep the maximum value | `max` | `max_atomic` | `max_atomic_std` | `max_atomic_cuda` |
|
||||
| Increment via compare-and-swap | `cas` | `cas_atomic` | `cas_atomic_std` | `cas_atomic_cuda` |
|
||||
|
||||
The non-atomic kernels perform the read-modify-write as separate steps, so concurrent threads interleave and lose updates. The atomic kernels perform it as one indivisible hardware operation and produce the exact expected result every time.
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
- What a race condition looks like: the non-atomic results are dramatically (add, CAS) or subtly (max) wrong
|
||||
- Using the atomic intrinsics `atomicAdd`, `atomicMax`, and `atomicCAS` on global memory
|
||||
- Building an atomic operation from an `atomicCAS` retry loop — the pattern that can implement any read-modify-write atomically
|
||||
- Using `cuda::std::atomic_ref` (libcu++/CCCL) to write the same atomics with the standard `std::atomic` API in device code
|
||||
- Using `cuda::atomic_ref` (CCCL) — the CUDA-specific variant that shares the same API as `cuda::std::atomic_ref` but natively supports CUDA thread scopes
|
||||
- Why dedicated intrinsics beat CAS loops under contention (compare the `cas_atomic` timing against `increment_atomic`)
|
||||
- Timing GPU work with CUDA events (`cudaEventRecord` / `cudaEventElapsedTime`)
|
||||
- Resetting device buffers between runs with `cudaMemset`
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Atomic Intrinsics
|
||||
- **Race condition** — a plain `g[i] = g[i] + 1` is three steps (read, modify, write); two threads can read the same old value and one increment is lost
|
||||
- **Atomic read-modify-write** — the hardware serializes atomic updates to the same address (performed at the L2 cache), so no update is lost
|
||||
- **Compare-and-swap (CAS)** — `atomicCAS(addr, expected, desired)` swaps only if the current value equals `expected` and returns the value it found; looping until the swap succeeds makes any operation atomic
|
||||
- **Contention cost** — atomics are correct but serialize colliding threads; the CAS retry loop shows this at its most extreme
|
||||
|
||||
## Supported SM Architectures
|
||||
## Key APIs
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
### CUDA Device Intrinsics
|
||||
- `atomicAdd` — atomically add a value to a memory location
|
||||
- `atomicMax` — atomically store the maximum of the current and a proposed value
|
||||
- `atomicCAS` — atomically compare-and-swap; returns the previous value
|
||||
|
||||
## Supported OSes
|
||||
### libcu++ (CCCL)
|
||||
- `cuda::std::atomic_ref<int>` — wraps plain memory with the standard `std::atomic` interface, usable in device code; header: `<cuda/std/atomic>`
|
||||
- `cuda::atomic_ref<int>` — CUDA-specific version of `std::atomic_ref`; same API but natively supports CUDA thread scopes (e.g. `cuda::thread_scope_device`); header: `<cuda/atomic>`
|
||||
- `fetch_add` — atomic add, `std::atomic` style
|
||||
- `load` / `compare_exchange_weak` — the standard CAS retry loop; used to build max (which `std::atomic` lacks) and the CAS increment
|
||||
|
||||
Linux, Windows, QNX
|
||||
### CUDA Runtime
|
||||
- `cudaMalloc` / `cudaFree` — allocate and release device memory
|
||||
- `cudaMemset` — fill device memory with a byte value (zero the array between runs)
|
||||
- `cudaMemcpy` — copy results back to the host
|
||||
- `cudaEventCreate` / `cudaEventRecord` / `cudaEventSynchronize` / `cudaEventElapsedTime` / `cudaEventDestroy` — GPU-timeline timing of each kernel
|
||||
|
||||
## Supported CPU Architecture
|
||||
## Requirements
|
||||
|
||||
x86_64, armv7l, aarch64
|
||||
### Hardware
|
||||
- NVIDIA GPU with Compute Capability 7.5 or higher
|
||||
|
||||
## CUDA APIs involved
|
||||
### Software
|
||||
- CUDA Toolkit
|
||||
- CMake 3.20 or newer
|
||||
- A C++17-capable host compiler
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaStreamCreateWithFlags, cudaFree, cudaMallocHost, cudaFreeHost, cudaStreamSynchronize, cudaMalloc, cudaMemcpyAsync
|
||||
## How to Build
|
||||
|
||||
## Prerequisites
|
||||
See the [top-level README](../../../README.md#building-cuda-samples) for full build instructions, including how to build all samples or a single sample standalone.
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
## How to Run
|
||||
|
||||
## References (for more details)
|
||||
```bash
|
||||
./simpleAtomicIntrinsics
|
||||
```
|
||||
|
||||
No command-line arguments are required. The sample always runs on device 0.
|
||||
|
||||
## Expected Output
|
||||
|
||||
```text
|
||||
=== Atomic vs. non-atomic operations (intrinsics, cuda::std::atomic_ref, cuda::atomic_ref) ===
|
||||
|
||||
GPU Device 0: with compute capability X.Y and Number of SMs <smCount>
|
||||
|
||||
1000000 total threads in 1000 blocks writing into 10 array elements
|
||||
|
||||
[add] expected: every element = 100000
|
||||
non-atomic (0.19 ms): { 13 13 13 13 13 13 13 13 13 13 }
|
||||
atomicAdd() (0.19 ms): { 100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 }
|
||||
cuda::std::atomic_ref::fetch_add() (0.26 ms): { 100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 }
|
||||
cuda::atomic_ref::fetch_add() (0.26 ms): { 100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 }
|
||||
|
||||
[max] expected: element i = 999990 + i
|
||||
non-atomic (0.013 ms): { 998070 998071 998072 998073 998064 998065 998066 998067 998068 998069 }
|
||||
atomicMax() (0.18 ms): { 999990 999991 999992 999993 999994 999995 999996 999997 999998 999999 }
|
||||
cuda::std::atomic_ref::compare_exchange_weak() (1.0 ms): { 999990 999991 999992 999993 999994 999995 999996 999997 999998 999999 }
|
||||
cuda::atomic_ref::compare_exchange_weak() (1.0 ms): { 999990 999991 999992 999993 999994 999995 999996 999997 999998 999999 }
|
||||
|
||||
[CAS] expected: every element = 100000
|
||||
non-atomic (0.014 ms): { 13 13 13 13 13 13 13 13 13 13 }
|
||||
atomicCAS() (1716 ms): { 100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 }
|
||||
cuda::std::atomic_ref::compare_exchange_weak() (9934 ms): { 100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 }
|
||||
cuda::atomic_ref::compare_exchange_weak() (9934 ms): { 100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 }
|
||||
|
||||
The non-atomic kernels lose updates when threads race on the same
|
||||
element; the atomic kernels match the expected values exactly.
|
||||
```
|
||||
|
||||
**Reading the numbers:**
|
||||
- The non-atomic values vary from run to run — that nondeterminism is the race condition itself
|
||||
- `max` is the sneakiest failure: 998070 looks plausible next to the correct 999990
|
||||
- `cas_atomic` is orders of magnitude slower than `increment_atomic` for the same result: with ~100,000 threads contending per element, almost every CAS attempt fails and retries — use the dedicated intrinsic when one exists
|
||||
- The `_std` and `_cuda` kernels are correct but slower than the intrinsics: both `cuda::std::atomic_ref` and `cuda::atomic_ref` default to sequentially-consistent ordering at system scope — a stronger guarantee than the relaxed device-scope intrinsics (which is why their timings match each other in every section above)
|
||||
|
||||
## Files
|
||||
|
||||
- `simpleAtomicIntrinsics.cu` — the twelve kernels and the host driver
|
||||
- `README.md` — this file
|
||||
- `CMakeLists.txt` — build configuration
|
||||
|
||||
## See Also
|
||||
|
||||
- [CUDA Programming Guide — Atomics](https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/writing-cuda-kernels.html#atomics)
|
||||
- [CUDA Programming Guide — Atomic Functions](https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/cpp-language-extensions.html#atomic-functions)
|
||||
- [CUDA Core Compute Libraries — cuda::atomic](https://nvidia.github.io/cccl/unstable/libcudacxx/extended_api/synchronization_primitives/atomic.html)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -25,110 +25,428 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* A simple program demonstrating trivial use of global memory atomic
|
||||
* device functions (atomic*() functions).
|
||||
/* A simple program demonstrating why atomic operations are needed, using the
|
||||
* atomic intrinsics atomicAdd, atomicMax, and atomicCAS.
|
||||
*
|
||||
* 1,000,000 threads write into an array of only 10 integers (each thread maps
|
||||
* to a slot with index % ARRAY_SIZE), so ~100,000 threads update every element
|
||||
* at the same time. A plain update like g[i] = g[i] + 1 is three separate
|
||||
* steps: read the value, modify it, write it back. When two threads interleave
|
||||
* (both read the same old value, both write back the same result), one update
|
||||
* silently overwrites the other and is lost. This is a race condition: the
|
||||
* result depends on thread timing, not program logic, and changes every run.
|
||||
*
|
||||
* Each operation is therefore run four ways: a non-atomic kernel that loses
|
||||
* most of its updates to these race conditions, and three atomic kernels that
|
||||
* do the read-modify-write as one indivisible operation and give the exact
|
||||
* result — first with the CUDA atomic intrinsics, then with
|
||||
* cuda::std::atomic_ref from CCCL (the std::atomic API in device code), and
|
||||
* finally with cuda::atomic_ref from CCCL (the CUDA-specific variant that
|
||||
* natively supports CUDA thread scopes).
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// Includes CUDA
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
// Utilities and timing functions
|
||||
#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h
|
||||
// Includes CCCL: std::atomic as usable in device code (cuda::std::atomic_ref)
|
||||
#include <cuda/std/atomic>
|
||||
|
||||
// CUDA helper functions
|
||||
#include <helper_cuda.h> // helper functions for CUDA error check
|
||||
// cuda::atomic_ref<T>: like cuda::std::atomic_ref but CUDA-specific, supports thread scopes
|
||||
#include <cuda/atomic>
|
||||
|
||||
// Includes, kernels
|
||||
#include "simpleAtomicIntrinsics_kernel.cuh"
|
||||
// and cuda::ceil_div for computing the launch grid size
|
||||
#include <cuda/cmath>
|
||||
|
||||
const char *sampleName = "simpleAtomicIntrinsics";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Auto-Verification Code
|
||||
bool testResult = true;
|
||||
// Launch configuration: many threads hammering a small array
|
||||
#define NUM_THREADS 1000000
|
||||
#define BLOCK_WIDTH 1000
|
||||
#define ARRAY_SIZE 10
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Declaration, forward
|
||||
void runTest(int argc, char **argv);
|
||||
// Increment without atomics: threads race on shared elements and lose updates
|
||||
__global__ void increment(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
// ceil_div rounds up the grid, this may launch extra threads
|
||||
// Adding this guard to ignore extra threads
|
||||
if (tid < NUM_THREADS) {
|
||||
// each thread increments one element, wrapping at ARRAY_SIZE
|
||||
int i = tid % ARRAY_SIZE;
|
||||
g[i] = g[i] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" bool computeGold(int *gpuData, const int len);
|
||||
// Increment with atomicAdd: read-modify-write is one indivisible operation
|
||||
__global__ void increment_atomic(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
atomicAdd(&g[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment with cuda::std::atomic_ref: wraps the plain int in g[] and exposes
|
||||
// the exact std::atomic API (fetch_add, load, store, ...) inside device code
|
||||
__global__ void increment_atomic_std(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
cuda::std::atomic_ref<int> ref(g[i]);
|
||||
ref.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment with cuda::atomic_ref: like cuda::std::atomic_ref but CUDA-specific
|
||||
// and natively supports CUDA thread scopes
|
||||
__global__ void increment_atomic_cuda(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
cuda::atomic_ref<int> ref(g[i]);
|
||||
ref.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Max without atomics: another thread can write between the compare and the
|
||||
// store, so a smaller value can overwrite a larger one
|
||||
__global__ void max(int *g)
|
||||
{
|
||||
// each thread contributes its global index as the value
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
if (g[i] < tid)
|
||||
g[i] = tid;
|
||||
}
|
||||
}
|
||||
|
||||
// Max with atomicMax: the compare and the store happen as one operation
|
||||
__global__ void max_atomic(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
atomicMax(&g[i], tid);
|
||||
}
|
||||
}
|
||||
|
||||
// Max with cuda::std::atomic_ref: std::atomic has no fetch_max, so max is
|
||||
// built the standard C++ way — a compare_exchange loop that stops as soon as
|
||||
// the stored value is already >= ours
|
||||
__global__ void max_atomic_std(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
|
||||
cuda::std::atomic_ref<int> ref(g[i]);
|
||||
int expected = ref.load();
|
||||
while (expected < tid && !ref.compare_exchange_weak(expected, tid)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Max with cuda::atomic_ref: same compare_exchange loop as the _std variant
|
||||
__global__ void max_atomic_cuda(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
|
||||
cuda::atomic_ref<int> ref(g[i]);
|
||||
int expected = ref.load();
|
||||
while (expected < tid && !ref.compare_exchange_weak(expected, tid)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increment written as compare-and-swap, without atomics: the value can change
|
||||
// between the compare and the swap, so increments are lost
|
||||
__global__ void cas(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
|
||||
int expected = g[i]; // read
|
||||
if (g[i] == expected) // compare
|
||||
g[i] = expected + 1; // swap (not atomic with the compare!)
|
||||
}
|
||||
}
|
||||
|
||||
// Increment with an atomicCAS retry loop: the classic pattern for building
|
||||
// any atomic operation out of compare-and-swap. atomicCAS returns the value
|
||||
// it found: if another thread interfered, the swap did not happen and we retry
|
||||
__global__ void cas_atomic(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
|
||||
int old = g[i];
|
||||
int assumed;
|
||||
do {
|
||||
assumed = old;
|
||||
old = atomicCAS(&g[i], assumed, assumed + 1);
|
||||
} while (old != assumed);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment with cuda::std::atomic_ref compare_exchange: the std::atomic way
|
||||
// to write a CAS retry loop. On failure, expected is updated with the value
|
||||
// actually found, so the loop just retries with fresh data
|
||||
__global__ void cas_atomic_std(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
|
||||
cuda::std::atomic_ref<int> ref(g[i]);
|
||||
int expected = ref.load();
|
||||
while (!ref.compare_exchange_weak(expected, expected + 1)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CAS increment with cuda::atomic_ref: same retry loop as the _std variant
|
||||
__global__ void cas_atomic_cuda(int *g)
|
||||
{
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (tid < NUM_THREADS) {
|
||||
int i = tid % ARRAY_SIZE;
|
||||
|
||||
cuda::atomic_ref<int> ref(g[i]);
|
||||
int expected = ref.load();
|
||||
while (!ref.compare_exchange_weak(expected, expected + 1)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print every element of the array
|
||||
void print_array(int *array, int size)
|
||||
{
|
||||
printf("{ ");
|
||||
for (int i = 0; i < size; i++)
|
||||
printf("%d ", array[i]);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Program main
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("%s starting...\n", sampleName);
|
||||
printf("=== Atomic vs. non-atomic operations (intrinsics, cuda::std::atomic_ref, cuda::atomic_ref) ===\n\n");
|
||||
|
||||
runTest(argc, argv);
|
||||
// Select device 0 as the active GPU
|
||||
int devID = 0;
|
||||
cudaSetDevice(devID);
|
||||
|
||||
printf("%s completed, returned %s\n", sampleName, testResult ? "OK" : "ERROR!");
|
||||
exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Run a simple test for CUDA
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void runTest(int argc, char **argv)
|
||||
{
|
||||
cudaStream_t stream;
|
||||
// This will pick the best possible CUDA capable device
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
StopWatchInterface *timer;
|
||||
sdkCreateTimer(&timer);
|
||||
sdkStartTimer(&timer);
|
||||
|
||||
unsigned int numThreads = 256;
|
||||
unsigned int numBlocks = 64;
|
||||
unsigned int numData = 11;
|
||||
unsigned int memSize = sizeof(int) * numData;
|
||||
|
||||
// allocate mem for the result on host side
|
||||
int *hOData;
|
||||
checkCudaErrors(cudaMallocHost(&hOData, memSize));
|
||||
|
||||
// initialize the memory
|
||||
for (unsigned int i = 0; i < numData; i++)
|
||||
hOData[i] = 0;
|
||||
|
||||
// To make the AND and XOR tests generate something other than 0...
|
||||
hOData[8] = hOData[10] = 0xff;
|
||||
|
||||
checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
|
||||
// allocate device memory for result
|
||||
int *dOData;
|
||||
checkCudaErrors(cudaMalloc((void **)&dOData, memSize));
|
||||
// copy host memory to device to initialize to zero
|
||||
checkCudaErrors(cudaMemcpyAsync(dOData, hOData, memSize, cudaMemcpyHostToDevice, stream));
|
||||
|
||||
// execute the kernel
|
||||
testKernel<<<numBlocks, numThreads, 0, stream>>>(dOData);
|
||||
|
||||
// Copy result from device to host
|
||||
checkCudaErrors(cudaMemcpyAsync(hOData, dOData, memSize, cudaMemcpyDeviceToHost, stream));
|
||||
checkCudaErrors(cudaStreamSynchronize(stream));
|
||||
|
||||
sdkStopTimer(&timer);
|
||||
printf("Processing time: %f (ms)\n", sdkGetTimerValue(&timer));
|
||||
sdkDeleteTimer(&timer);
|
||||
|
||||
// Compute reference solution
|
||||
testResult = computeGold(hOData, numThreads * numBlocks);
|
||||
|
||||
// Cleanup memory
|
||||
checkCudaErrors(cudaFreeHost(hOData));
|
||||
checkCudaErrors(cudaFree(dOData));
|
||||
// Query compute capability (major.minor) and number of SMs on the device
|
||||
int major = 0, minor = 0, smCount = 0;
|
||||
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devID);
|
||||
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, devID);
|
||||
cudaDeviceGetAttribute(&smCount, cudaDevAttrMultiProcessorCount, devID);
|
||||
|
||||
// Print device info
|
||||
printf("GPU Device %d: with compute capability %d.%d and Number of SMs %d\n\n", devID, major, minor, smCount);
|
||||
|
||||
// enough blocks to cover all threads, rounding up if not evenly divisible
|
||||
int numBlocks = cuda::ceil_div(NUM_THREADS, BLOCK_WIDTH);
|
||||
printf("%d total threads in %d blocks writing into %d array elements\n\n",
|
||||
NUM_THREADS, numBlocks, ARRAY_SIZE);
|
||||
|
||||
// declare and allocate host memory
|
||||
int h_array[ARRAY_SIZE];
|
||||
const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);
|
||||
|
||||
// declare and allocate GPU memory (zeroed with cudaMemset before each run)
|
||||
int *d_array;
|
||||
cudaMalloc((void **)&d_array, ARRAY_BYTES);
|
||||
|
||||
// CUDA events record timestamps on the GPU stream to measure device time
|
||||
cudaEvent_t start, stop;
|
||||
cudaEventCreate(&start);
|
||||
cudaEventCreate(&stop);
|
||||
float elapsed_ms;
|
||||
|
||||
// ----- add: every thread adds 1, each element should reach 100000 -----
|
||||
printf("[add] expected: every element = %d\n", NUM_THREADS / ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
increment<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-36s (%g ms): ", "non-atomic", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
increment_atomic<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-36s (%g ms): ", "atomicAdd()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
increment_atomic_std<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-36s (%g ms): ", "cuda::std::atomic_ref::fetch_add()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
increment_atomic_cuda<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-36s (%g ms): ", "cuda::atomic_ref::fetch_add()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
// ----- max: every thread offers its index, element i should reach
|
||||
// the largest index that maps to it: NUM_THREADS - ARRAY_SIZE + i -----
|
||||
printf("\n[max] expected: element i = %d + i\n", NUM_THREADS - ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
max<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "non-atomic", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
max_atomic<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "atomicMax()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
max_atomic_std<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "cuda::std::atomic_ref::compare_exchange_weak()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
max_atomic_cuda<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "cuda::atomic_ref::compare_exchange_weak()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
// ----- CAS: increment built from compare-and-swap, same expected
|
||||
// result as add -----
|
||||
printf("\n[CAS] expected: every element = %d\n", NUM_THREADS / ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
cas<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "non-atomic", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
cas_atomic<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "atomicCAS()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
cas_atomic_std<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "cuda::std::atomic_ref::compare_exchange_weak()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
cudaMemset((void *)d_array, 0, ARRAY_BYTES);
|
||||
|
||||
cudaEventRecord(start);
|
||||
cas_atomic_cuda<<<numBlocks, BLOCK_WIDTH>>>(d_array);
|
||||
cudaEventRecord(stop);
|
||||
|
||||
cudaEventSynchronize(stop);
|
||||
cudaEventElapsedTime(&elapsed_ms, start, stop);
|
||||
cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);
|
||||
|
||||
printf("%-46s (%g ms): ", "cuda::atomic_ref::compare_exchange_weak()", elapsed_ms);
|
||||
print_array(h_array, ARRAY_SIZE);
|
||||
|
||||
printf("\nThe non-atomic kernels lose updates when threads race on the same\n");
|
||||
printf("element; the atomic kernels match the expected values exactly.\n");
|
||||
|
||||
// free GPU memory allocation and timing events, then exit
|
||||
cudaEventDestroy(start);
|
||||
cudaEventDestroy(stop);
|
||||
cudaFree(d_array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define min(a, b) (a) < (b) ? (a) : (b)
|
||||
#define max(a, b) (a) > (b) ? (a) : (b)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// export C interface
|
||||
extern "C" int computeGold(int *gpuData, const int len);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Compute reference data set
|
||||
//! Each element is multiplied with the number of threads / array length
|
||||
//! @param reference reference data, computed but preallocated
|
||||
//! @param idata input data as provided to device
|
||||
//! @param len number of elements in reference / idata
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int computeGold(int *gpuData, const int len)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val += 10;
|
||||
}
|
||||
|
||||
if (val != gpuData[0]) {
|
||||
printf("atomicAdd failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val -= 10;
|
||||
}
|
||||
|
||||
if (val != gpuData[1]) {
|
||||
printf("atomicSub failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// third element should be a member of [0, len)
|
||||
if (i == gpuData[2]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("atomicExch failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = -(1 << 8);
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// fourth element should be len-1
|
||||
val = max(val, i);
|
||||
}
|
||||
|
||||
if (val != gpuData[3]) {
|
||||
printf("atomicMax failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 1 << 8;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val = min(val, i);
|
||||
}
|
||||
|
||||
if (val != gpuData[4]) {
|
||||
printf("atomicMin failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int limit = 17;
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val = (val >= limit) ? 0 : val + 1;
|
||||
}
|
||||
|
||||
if (val != gpuData[5]) {
|
||||
printf("atomicInc failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
limit = 137;
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val = ((val == 0) || (val > limit)) ? limit : val - 1;
|
||||
}
|
||||
|
||||
if (val != gpuData[6]) {
|
||||
printf("atomicDec failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
found = false;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// eighth element should be a member of [0, len)
|
||||
if (i == gpuData[7]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("atomicCAS failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0xff;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// 9th element should be 1
|
||||
val &= (2 * i + 7);
|
||||
}
|
||||
|
||||
if (val != gpuData[8]) {
|
||||
printf("atomicAnd failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// 10th element should be 0xff
|
||||
val |= (1 << i);
|
||||
}
|
||||
|
||||
if (val != gpuData[9]) {
|
||||
printf("atomicOr failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0xff;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// 11th element should be 0xff
|
||||
val ^= i;
|
||||
}
|
||||
|
||||
if (val != gpuData[10]) {
|
||||
printf("atomicXor failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
/* Simple kernel demonstrating atomic functions in device code. */
|
||||
|
||||
#ifndef _SIMPLEATOMICS_KERNEL_H_
|
||||
#define _SIMPLEATOMICS_KERNEL_H_
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Simple test kernel for atomic instructions
|
||||
//! @param g_idata input data in global memory
|
||||
//! @param g_odata output data in global memory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__global__ void testKernel(int *g_odata)
|
||||
{
|
||||
// access thread id
|
||||
const unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
|
||||
// Test various atomic instructions
|
||||
|
||||
// Arithmetic atomic instructions
|
||||
|
||||
// Atomic addition
|
||||
atomicAdd(&g_odata[0], 10);
|
||||
|
||||
// Atomic subtraction (final should be 0)
|
||||
atomicSub(&g_odata[1], 10);
|
||||
|
||||
// Atomic exchange
|
||||
atomicExch(&g_odata[2], tid);
|
||||
|
||||
// Atomic maximum
|
||||
atomicMax(&g_odata[3], tid);
|
||||
|
||||
// Atomic minimum
|
||||
atomicMin(&g_odata[4], tid);
|
||||
|
||||
// Atomic increment (modulo 17+1)
|
||||
atomicInc((unsigned int *)&g_odata[5], 17);
|
||||
|
||||
// Atomic decrement
|
||||
atomicDec((unsigned int *)&g_odata[6], 137);
|
||||
|
||||
// Atomic compare-and-swap
|
||||
atomicCAS(&g_odata[7], tid - 1, tid);
|
||||
|
||||
// Bitwise atomic instructions
|
||||
|
||||
// Atomic AND
|
||||
atomicAnd(&g_odata[8], 2 * tid + 7);
|
||||
|
||||
// Atomic OR
|
||||
atomicOr(&g_odata[9], 1 << tid);
|
||||
|
||||
// Atomic XOR
|
||||
atomicXor(&g_odata[10], tid);
|
||||
}
|
||||
|
||||
#endif // #ifndef _SIMPLEATOMICS_KERNEL_H_
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(simpleAtomicIntrinsics_nvrtc LANGUAGES C CXX CUDA)
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add sample target executable
|
||||
add_executable(simpleAtomicIntrinsics_nvrtc simpleAtomicIntrinsics_cpu.cpp simpleAtomicIntrinsics.cpp)
|
||||
|
||||
target_compile_options(simpleAtomicIntrinsics_nvrtc PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleAtomicIntrinsics_nvrtc PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
target_link_libraries(simpleAtomicIntrinsics_nvrtc PRIVATE
|
||||
CUDA::nvrtc
|
||||
CUDA::cuda_driver
|
||||
)
|
||||
|
||||
# Copy clock_kernel.cu to the output directory
|
||||
add_custom_command(TARGET simpleAtomicIntrinsics_nvrtc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/simpleAtomicIntrinsics_kernel.cuh
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
@@ -1,39 +0,0 @@
|
||||
# simpleAtomicIntrinsics_nvrtc - Simple Atomic Intrinsics with libNVRTC
|
||||
|
||||
## Description
|
||||
|
||||
A simple demonstration of global memory atomic instructions.This sample makes use of NVRTC for Runtime Compilation.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Atomic Intrinsics, Runtime Compilation
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows, QNX
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuMemcpyDtoH, cuLaunchKernel, cuMemcpyHtoD, cuCtxSynchronize, cuMemAlloc, cuMemFree, cuModuleGetFunction
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaBlockSize, cudaGridSize
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[NVRTC](../../../README.md#nvrtc)
|
||||
|
||||
## 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)
|
||||
@@ -1,153 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
/* A simple program demonstrating trivial use of global memory atomic
|
||||
* device functions (atomic*() functions).
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// Includes CUDA
|
||||
#include <cuda_runtime.h>
|
||||
#include <nvrtc_helper.h>
|
||||
|
||||
// Utilities and timing functions
|
||||
#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h
|
||||
|
||||
const char *sampleName = "simpleAtomicIntrinsics_nvrtc";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Auto-Verification Code
|
||||
bool testResult = true;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Declaration, forward
|
||||
void runTest(int argc, char **argv);
|
||||
|
||||
extern "C" bool computeGold(int *gpuData, const int len);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Program main
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("%s starting...\n", sampleName);
|
||||
|
||||
runTest(argc, argv);
|
||||
|
||||
printf("%s completed, returned %s\n", sampleName, testResult ? "OK" : "ERROR!");
|
||||
|
||||
exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Run a simple test for CUDA
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void runTest(int argc, char **argv)
|
||||
{
|
||||
int dev = 0;
|
||||
|
||||
char *cubin, *kernel_file;
|
||||
size_t cubinSize;
|
||||
|
||||
kernel_file = sdkFindFilePath("simpleAtomicIntrinsics_kernel.cuh", argv[0]);
|
||||
compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
|
||||
|
||||
CUmodule module = loadCUBIN(cubin, argc, argv);
|
||||
CUfunction kernel_addr;
|
||||
|
||||
checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "testKernel"));
|
||||
|
||||
StopWatchInterface *timer;
|
||||
sdkCreateTimer(&timer);
|
||||
sdkStartTimer(&timer);
|
||||
|
||||
unsigned int numThreads = 256;
|
||||
unsigned int numBlocks = 64;
|
||||
unsigned int numData = 11;
|
||||
unsigned int memSize = sizeof(int) * numData;
|
||||
|
||||
// allocate mem for the result on host side
|
||||
int *hOData = (int *)malloc(memSize);
|
||||
|
||||
// initialize the memory
|
||||
for (unsigned int i = 0; i < numData; i++)
|
||||
hOData[i] = 0;
|
||||
|
||||
// To make the AND and XOR tests generate something other than 0...
|
||||
hOData[8] = hOData[10] = 0xff;
|
||||
|
||||
// allocate device memory for result
|
||||
CUdeviceptr dOData;
|
||||
checkCudaErrors(cuMemAlloc(&dOData, memSize));
|
||||
checkCudaErrors(cuMemcpyHtoD(dOData, hOData, memSize));
|
||||
|
||||
// execute the kernel
|
||||
dim3 cudaBlockSize(numThreads, 1, 1);
|
||||
dim3 cudaGridSize(numBlocks, 1, 1);
|
||||
|
||||
void *arr[] = {(void *)&dOData};
|
||||
checkCudaErrors(cuLaunchKernel(kernel_addr,
|
||||
cudaGridSize.x,
|
||||
cudaGridSize.y,
|
||||
cudaGridSize.z, /* grid dim */
|
||||
cudaBlockSize.x,
|
||||
cudaBlockSize.y,
|
||||
cudaBlockSize.z, /* block dim */
|
||||
0,
|
||||
0, /* shared mem, stream */
|
||||
&arr[0], /* arguments */
|
||||
0));
|
||||
|
||||
checkCudaErrors(cuCtxSynchronize());
|
||||
|
||||
checkCudaErrors(cuMemcpyDtoH(hOData, dOData, memSize));
|
||||
|
||||
// Copy result from device to host
|
||||
sdkStopTimer(&timer);
|
||||
printf("Processing time: %f (ms)\n", sdkGetTimerValue(&timer));
|
||||
sdkDeleteTimer(&timer);
|
||||
|
||||
// Compute reference solution
|
||||
testResult = computeGold(hOData, numThreads * numBlocks);
|
||||
|
||||
// Cleanup memory
|
||||
free(hOData);
|
||||
checkCudaErrors(cuMemFree(dOData));
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define min(a, b) (a) < (b) ? (a) : (b)
|
||||
#define max(a, b) (a) > (b) ? (a) : (b)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// export C interface
|
||||
extern "C" int computeGold(int *gpuData, const int len);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Compute reference data set
|
||||
//! Each element is multiplied with the number of threads / array length
|
||||
//! @param reference reference data, computed but preallocated
|
||||
//! @param idata input data as provided to device
|
||||
//! @param len number of elements in reference / idata
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int computeGold(int *gpuData, const int len)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val += 10;
|
||||
}
|
||||
|
||||
if (val != gpuData[0]) {
|
||||
printf("atomicAdd failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val -= 10;
|
||||
}
|
||||
|
||||
if (val != gpuData[1]) {
|
||||
printf("atomicSub failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// third element should be a member of [0, len)
|
||||
if (i == gpuData[2]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("atomicExch failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = -(1 << 8);
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// fourth element should be len-1
|
||||
val = max(val, i);
|
||||
}
|
||||
|
||||
if (val != gpuData[3]) {
|
||||
printf("atomicMax failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 1 << 8;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val = min(val, i);
|
||||
}
|
||||
|
||||
if (val != gpuData[4]) {
|
||||
printf("atomicMin failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int limit = 17;
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val = (val >= limit) ? 0 : val + 1;
|
||||
}
|
||||
|
||||
if (val != gpuData[5]) {
|
||||
printf("atomicInc failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
limit = 137;
|
||||
val = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
val = ((val == 0) || (val > limit)) ? limit : val - 1;
|
||||
}
|
||||
|
||||
if (val != gpuData[6]) {
|
||||
printf("atomicDec failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
found = false;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// eighth element should be a member of [0, len)
|
||||
if (i == gpuData[7]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("atomicCAS failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0xff;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// 9th element should be 1
|
||||
val &= (2 * i + 7);
|
||||
}
|
||||
|
||||
if (val != gpuData[8]) {
|
||||
printf("atomicAnd failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// 10th element should be 0xff
|
||||
val |= (1 << i);
|
||||
}
|
||||
|
||||
if (val != gpuData[9]) {
|
||||
printf("atomicOr failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
val = 0xff;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// 11th element should be 0xff
|
||||
val ^= i;
|
||||
}
|
||||
|
||||
if (val != gpuData[10]) {
|
||||
printf("atomicXor failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
/* Simple kernel demonstrating atomic functions in device code. */
|
||||
|
||||
#ifndef _SIMPLEATOMICS_KERNEL_H_
|
||||
#define _SIMPLEATOMICS_KERNEL_H_
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Simple test kernel for atomic instructions
|
||||
//! @param g_idata input data in global memory
|
||||
//! @param g_odata output data in global memory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern "C" __global__ void testKernel(int *g_odata)
|
||||
{
|
||||
// access thread id
|
||||
const unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
|
||||
// Test various atomic instructions
|
||||
// Arithmetic atomic instructions
|
||||
// Atomic addition
|
||||
atomicAdd(&g_odata[0], 10);
|
||||
|
||||
// Atomic subtraction (final should be 0)
|
||||
atomicSub(&g_odata[1], 10);
|
||||
|
||||
// Atomic exchange
|
||||
atomicExch(&g_odata[2], tid);
|
||||
|
||||
// Atomic maximum
|
||||
atomicMax(&g_odata[3], tid);
|
||||
|
||||
// Atomic minimum
|
||||
atomicMin(&g_odata[4], tid);
|
||||
|
||||
// Atomic increment (modulo 17+1)
|
||||
atomicInc((unsigned int *)&g_odata[5], 17);
|
||||
|
||||
// Atomic decrement
|
||||
atomicDec((unsigned int *)&g_odata[6], 137);
|
||||
|
||||
// Atomic compare-and-swap
|
||||
atomicCAS(&g_odata[7], tid - 1, tid);
|
||||
|
||||
// Bitwise atomic instructions
|
||||
// Atomic AND
|
||||
atomicAnd(&g_odata[8], 2 * tid + 7);
|
||||
|
||||
// Atomic OR
|
||||
atomicOr(&g_odata[9], 1 << tid);
|
||||
|
||||
// Atomic XOR
|
||||
atomicXor(&g_odata[10], tid);
|
||||
}
|
||||
|
||||
#endif // #ifndef _SIMPLEATOMICS_KERNEL_H_
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleAttributes LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for simpleAttributes
|
||||
add_executable(simpleAttributes simpleAttributes.cu)
|
||||
|
||||
target_compile_options(simpleAttributes PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleAttributes PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleAttributes PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleCUDA2GL LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
if(WIN32)
|
||||
@@ -30,31 +26,22 @@ if(WIN32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
find_package(OpenGL)
|
||||
find_package(GLUT)
|
||||
|
||||
# Source file
|
||||
if(${OpenGL_FOUND})
|
||||
if (${GLUT_FOUND})
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
message(STATUS "Will not build sample simpleCUDA2GL - not supported on aarch64")
|
||||
else()
|
||||
# Add target for simpleCUDA2GL
|
||||
add_executable(simpleCUDA2GL simpleCUDA2GL.cu main.cpp)
|
||||
|
||||
target_compile_options(simpleCUDA2GL PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleCUDA2GL PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleCUDA2GL PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(simpleCUDA2GL PUBLIC
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${GLUT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(simpleCUDA2GL
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLUT_LIBRARIES}
|
||||
@@ -64,14 +51,12 @@ if(${OpenGL_FOUND})
|
||||
${PC_GLUT_LIBRARY_DIRS}/freeglut.lib
|
||||
${PC_GLUT_LIBRARY_DIRS}/${GLEW_LIB_NAME}.lib
|
||||
)
|
||||
|
||||
add_custom_command(TARGET simpleCUDA2GL
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/freeglut.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
|
||||
add_custom_command(TARGET simpleCUDA2GL
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
@@ -79,6 +64,8 @@ if(${OpenGL_FOUND})
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
endif()
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "GLUT not found - will not build sample 'simpleCUDA2GL'")
|
||||
@@ -86,7 +73,3 @@ if(${OpenGL_FOUND})
|
||||
else()
|
||||
message(STATUS "OpenGL not found - will not build sample 'simpleCUDA2GL'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleCallback LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for simpleCallback
|
||||
add_executable(simpleCallback simpleCallback.cu multithreading.cpp)
|
||||
add_executable(simpleCallback simpleCallback.cu)
|
||||
|
||||
target_compile_options(simpleCallback PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleCallback PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleCallback PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
# std::thread is backed by pthreads on Linux
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(simpleCallback PRIVATE Threads::Threads)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,32 +1,133 @@
|
||||
# simpleCallback - Simple CUDA Callbacks
|
||||
# Sample: simpleCallback
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements multi-threaded heterogeneous computing workloads with the new CPU callbacks for CUDA streams and events introduced with CUDA 5.0.
|
||||
A heterogeneous pipeline demo: **CPU pre-process → GPU kernel → CPU post-process**, all
|
||||
coordinated by a single CUDA stream. A worker thread (`std::thread`) fills the input buffer,
|
||||
enqueues the GPU work (H2D copy, kernel, D2H copy), and registers a host function with
|
||||
`cudaLaunchHostFunc` — the modern replacement for the deprecated `cudaStreamAddCallback`.
|
||||
CUDA invokes that host function automatically once all preceding stream work completes,
|
||||
so the CPU post-processing (result verification) runs without the main thread ever polling.
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
- Building a CPU → GPU → CPU pipeline ordered by a single CUDA stream
|
||||
- Scheduling CPU post-processing on a stream with `cudaLaunchHostFunc`
|
||||
- Enqueuing GPU work from a `std::thread` worker — all host threads share the device's primary context
|
||||
- The rule that a host function must never call CUDA APIs
|
||||
- Why pinned host memory (`cudaMallocHost`) is required for `cudaMemcpyAsync` to be truly asynchronous
|
||||
- Querying GPU properties (compute capability, SM count) with `cudaDeviceGetAttribute`
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CUDA Streams, Callback Functions, Multithreading
|
||||
### Stream-Ordered Pipeline
|
||||
|
||||
## Supported SM Architectures
|
||||
A stream executes its operations strictly in order. The worker thread pushes the whole
|
||||
pipeline onto one stream and CUDA handles every dependency:
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](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.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
```text
|
||||
stream: [H2D copy] → [incrementKernel] → [D2H copy] → [postprocess host func]
|
||||
```
|
||||
|
||||
## Supported OSes
|
||||
The main thread only has to `cudaStreamSynchronize` once at the end — when the stream
|
||||
drains, the results are already verified.
|
||||
|
||||
Linux, Windows
|
||||
### Host Functions (`cudaLaunchHostFunc`)
|
||||
|
||||
## Supported CPU Architecture
|
||||
A host function is a CPU callback enqueued on a stream like any other operation. CUDA runs
|
||||
it on an internal thread once all prior work in the stream has finished. Two rules apply:
|
||||
|
||||
x86_64, armv7l
|
||||
- It must **not** call any CUDA runtime or driver API (no `cudaFree`, no kernel launches)
|
||||
- It should be short — the stream cannot proceed until it returns
|
||||
|
||||
## CUDA APIs involved
|
||||
### Threads Share the Primary Context
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaHostAlloc, cudaStreamDestroy, cudaFree, cudaSetDevice, cudaGetDeviceCount, cudaFreeHost, cudaStreamAddCallback, cudaMalloc, cudaMemcpyAsync, cudaStreamCreate, cudaGetDeviceProperties
|
||||
With the CUDA runtime API, every host thread in the process shares the device's primary
|
||||
context. That is why the worker thread can enqueue copies and kernels onto a stream that
|
||||
`main` created.
|
||||
|
||||
## Prerequisites
|
||||
### Pinned Host Memory
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
`cudaMemcpyAsync` only runs *truly asynchronously* when the host buffer is page-locked (pinned),
|
||||
so the OS cannot move the pages mid-transfer; with ordinary pageable memory the copy falls back to
|
||||
synchronous behavior. The sample allocates the host buffer with `cudaMallocHost`.
|
||||
|
||||
## References (for more details)
|
||||
## Key APIs
|
||||
|
||||
### CUDA Runtime
|
||||
|
||||
- `cudaSetDevice` — select the active GPU for all subsequent CUDA calls
|
||||
- `cudaDeviceGetAttribute` — query compute capability and SM count
|
||||
- `cudaMallocHost` / `cudaFreeHost` — allocate and free pinned host memory
|
||||
- `cudaMalloc` / `cudaFree` — allocate and free device memory
|
||||
- `cudaStreamCreate` / `cudaStreamDestroy` — create and destroy the stream that orders the pipeline
|
||||
- `cudaMemcpyAsync` — non-blocking H2D and D2H transfers on the stream
|
||||
- `cudaLaunchHostFunc` — enqueue a CPU callback on the stream
|
||||
- `cudaStreamSynchronize` — block the host until the stream (including the host function) is done
|
||||
|
||||
### C++ Standard Library
|
||||
|
||||
- `std::thread` / `join()` — worker-thread creation
|
||||
|
||||
## Requirements
|
||||
|
||||
### Hardware
|
||||
|
||||
- NVIDIA GPU with Compute Capability 7.5 or higher
|
||||
|
||||
### Software
|
||||
|
||||
- CMake 3.20 or newer
|
||||
- A C++17-capable host compiler
|
||||
|
||||
## How to Build
|
||||
|
||||
See the [top-level README](../../../README.md#building-cuda-samples) for full build instructions, including how to build all samples or a single sample standalone.
|
||||
|
||||
## How to Run
|
||||
|
||||
```bash
|
||||
./simpleCallback
|
||||
```
|
||||
|
||||
No command-line arguments are required. The sample always runs on device 0.
|
||||
|
||||
## Expected Output
|
||||
|
||||
```text
|
||||
=====================================================
|
||||
simpleCallback: CPU -> GPU -> CPU pipeline demo
|
||||
=====================================================
|
||||
Using GPU 0: compute capability X.Y, <smCount> SMs
|
||||
[thread] Stage 1: CPU pre-processing on a worker thread...
|
||||
[thread] Filled 100000 elements. First 3 inputs: 42, 43, 44
|
||||
|
||||
[thread] Stage 2: enqueuing H2D copy, kernel, D2H copy on the stream
|
||||
[thread] Registering post-processing callback with cudaLaunchHostFunc
|
||||
|
||||
[main] Worker thread joined; GPU work has been enqueued.
|
||||
[main] Waiting for the stream (and callback) to finish...
|
||||
|
||||
[host func] Stage 3: callback fired automatically - the GPU work is done!
|
||||
[host func] First 3 results: 43, 44, 45 (each input +1)
|
||||
[host func] Verified all 100000 results: PASS
|
||||
|
||||
[main] Pipeline complete. Result: SUCCESS
|
||||
```
|
||||
|
||||
**Reading the output:**
|
||||
- Inputs start at 42 because each element is `workload.id + i` with `id = 42`
|
||||
- Each result is its input plus one — the kernel's only job
|
||||
- The `[host func]` lines print from a CUDA internal thread, not from `main`
|
||||
|
||||
## Files
|
||||
|
||||
- `simpleCallback.cu` — kernel, worker-thread function, host-function callback, and main driver
|
||||
- `README.md` — this file
|
||||
- `CMakeLists.txt` — build configuration
|
||||
|
||||
## See Also
|
||||
|
||||
- [CUDA Programming Guide — Callback Functions from Streams](https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/asynchronous-execution.html#callback-functions-from-streams)
|
||||
- [CUDA Programming Guide — CUDA Streams](https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/asynchronous-execution.html#cuda-streams)
|
||||
- [CUDA Runtime API — Execution Control (cudaLaunchHostFunc)](https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__EXECUTION.html#group__CUDART__EXECUTION_1g05841eaa5f90f27124241baafb3e856f)
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include "multithreading.h"
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
// Create thread
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data)
|
||||
{
|
||||
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
|
||||
}
|
||||
|
||||
// Wait for thread to finish
|
||||
void cutEndThread(CUTThread thread)
|
||||
{
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
CloseHandle(thread);
|
||||
}
|
||||
|
||||
// Wait for multiple threads
|
||||
void cutWaitForThreads(const CUTThread *threads, int num)
|
||||
{
|
||||
WaitForMultipleObjects(num, threads, true, INFINITE);
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
CloseHandle(threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create barrier.
|
||||
CUTBarrier cutCreateBarrier(int releaseCount)
|
||||
{
|
||||
CUTBarrier barrier;
|
||||
|
||||
InitializeCriticalSection(&barrier.criticalSection);
|
||||
barrier.barrierEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("BarrierEvent"));
|
||||
barrier.count = 0;
|
||||
barrier.releaseCount = releaseCount;
|
||||
|
||||
return barrier;
|
||||
}
|
||||
|
||||
// Increment barrier. (execution continues)
|
||||
void cutIncrementBarrier(CUTBarrier *barrier)
|
||||
{
|
||||
int myBarrierCount;
|
||||
EnterCriticalSection(&barrier->criticalSection);
|
||||
myBarrierCount = ++barrier->count;
|
||||
LeaveCriticalSection(&barrier->criticalSection);
|
||||
|
||||
if (myBarrierCount >= barrier->releaseCount) {
|
||||
SetEvent(barrier->barrierEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for barrier release.
|
||||
void cutWaitForBarrier(CUTBarrier *barrier) { WaitForSingleObject(barrier->barrierEvent, INFINITE); }
|
||||
|
||||
// Destroy barrier
|
||||
void cutDestroyBarrier(CUTBarrier *barrier) {}
|
||||
|
||||
#else
|
||||
// Create thread
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE func, void *data)
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, func, data);
|
||||
return thread;
|
||||
}
|
||||
|
||||
// Wait for thread to finish
|
||||
void cutEndThread(CUTThread thread) { pthread_join(thread, NULL); }
|
||||
|
||||
// Wait for multiple threads
|
||||
void cutWaitForThreads(const CUTThread *threads, int num)
|
||||
{
|
||||
for (int i = 0; i < num; i++) {
|
||||
cutEndThread(threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create barrier.
|
||||
CUTBarrier cutCreateBarrier(int releaseCount)
|
||||
{
|
||||
CUTBarrier barrier;
|
||||
|
||||
barrier.count = 0;
|
||||
barrier.releaseCount = releaseCount;
|
||||
|
||||
pthread_mutex_init(&barrier.mutex, 0);
|
||||
pthread_cond_init(&barrier.conditionVariable, 0);
|
||||
|
||||
return barrier;
|
||||
}
|
||||
|
||||
// Increment barrier. (execution continues)
|
||||
void cutIncrementBarrier(CUTBarrier *barrier)
|
||||
{
|
||||
int myBarrierCount;
|
||||
pthread_mutex_lock(&barrier->mutex);
|
||||
myBarrierCount = ++barrier->count;
|
||||
pthread_mutex_unlock(&barrier->mutex);
|
||||
|
||||
if (myBarrierCount >= barrier->releaseCount) {
|
||||
pthread_cond_signal(&barrier->conditionVariable);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for barrier release.
|
||||
void cutWaitForBarrier(CUTBarrier *barrier)
|
||||
{
|
||||
pthread_mutex_lock(&barrier->mutex);
|
||||
|
||||
while (barrier->count < barrier->releaseCount) {
|
||||
pthread_cond_wait(&barrier->conditionVariable, &barrier->mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&barrier->mutex);
|
||||
}
|
||||
|
||||
// Destroy barrier
|
||||
void cutDestroyBarrier(CUTBarrier *barrier)
|
||||
{
|
||||
pthread_mutex_destroy(&barrier->mutex);
|
||||
pthread_cond_destroy(&barrier->conditionVariable);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
/* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#ifndef MULTITHREADING_H
|
||||
#define MULTITHREADING_H
|
||||
|
||||
// Simple portable thread library.
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
// Windows threads.
|
||||
#include <windows.h>
|
||||
|
||||
typedef HANDLE CUTThread;
|
||||
typedef unsigned(WINAPI *CUT_THREADROUTINE)(void *);
|
||||
|
||||
struct CUTBarrier
|
||||
{
|
||||
CRITICAL_SECTION criticalSection;
|
||||
HANDLE barrierEvent;
|
||||
int releaseCount;
|
||||
int count;
|
||||
};
|
||||
|
||||
#define CUT_THREADPROC unsigned WINAPI
|
||||
#define CUT_THREADEND return 0
|
||||
|
||||
#else
|
||||
// POSIX threads.
|
||||
#include <pthread.h>
|
||||
|
||||
typedef pthread_t CUTThread;
|
||||
typedef void *(*CUT_THREADROUTINE)(void *);
|
||||
|
||||
#define CUT_THREADPROC void *
|
||||
#define CUT_THREADEND return 0
|
||||
|
||||
struct CUTBarrier
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t conditionVariable;
|
||||
int releaseCount;
|
||||
int count;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// Create thread.
|
||||
CUTThread cutStartThread(CUT_THREADROUTINE, void *data);
|
||||
|
||||
// Wait for thread to finish.
|
||||
void cutEndThread(CUTThread thread);
|
||||
|
||||
// Wait for multiple threads.
|
||||
void cutWaitForThreads(const CUTThread *threads, int num);
|
||||
|
||||
// Create barrier.
|
||||
CUTBarrier cutCreateBarrier(int releaseCount);
|
||||
|
||||
// Increment barrier. (execution continues)
|
||||
void cutIncrementBarrier(CUTBarrier *barrier);
|
||||
|
||||
// Wait for barrier release.
|
||||
void cutWaitForBarrier(CUTBarrier *barrier);
|
||||
|
||||
// Destroy barrier
|
||||
void cutDestroyBarrier(CUTBarrier *barrier);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // MULTITHREADING_H
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -26,190 +26,171 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* This sample implements multi-threaded heterogeneous computing workloads with
|
||||
* the new CPU callbacks for CUDA streams and events introduced with CUDA 5.0.
|
||||
* Together with the thread safety of the CUDA API implementing heterogeneous
|
||||
* workloads that float between CPU threads and GPUs has become simple and
|
||||
* efficient.
|
||||
* simpleCallback
|
||||
* --------------
|
||||
* An example of a heterogeneous pipeline in the form:
|
||||
*
|
||||
* The workloads in the sample follow the form CPU preprocess -> GPU process ->
|
||||
* CPU postprocess.
|
||||
* Each CPU processing step is handled by its own dedicated thread. GPU
|
||||
* workloads are sent to all available GPUs in the system.
|
||||
* CPU pre-process -> GPU kernel -> CPU post-process
|
||||
*
|
||||
* The whole pipeline is coordinated by a *single* CUDA stream. A stream
|
||||
* executes the operations enqueued on it in order, so we can simply push work
|
||||
* onto it and let CUDA take care of ordering and dependencies for us.
|
||||
*
|
||||
* Two different "CPU" mechanisms appear in this sample so you can see how they
|
||||
* differ:
|
||||
*
|
||||
* 1. The CPU pre-processing runs on a dedicated worker thread created with
|
||||
* C++ std::thread. This shows how ordinary host-side threading can be
|
||||
* combined with CUDA. The worker fills the input buffer and then enqueues
|
||||
* the GPU pipeline itself; main joins it and waits on the stream.
|
||||
*
|
||||
* 2. The CPU post-processing is scheduled *on the stream itself* with
|
||||
* cudaLaunchHostFunc(). CUDA calls our host function automatically once all
|
||||
* the preceding stream work (copies + kernel) has completed. This is the
|
||||
* modern, non-deprecated replacement for cudaStreamAddCallback().
|
||||
*
|
||||
* IMPORTANT rule for cudaLaunchHostFunc callbacks:
|
||||
* The host function must NOT call any CUDA runtime/driver API (no cudaMalloc,
|
||||
* cudaFree, kernel launches, etc.). It should only do plain CPU work.
|
||||
*
|
||||
* NOTE: For clarity this sample intentionally omits CUDA error checking. Real
|
||||
* applications should check the return code of every CUDA call.
|
||||
*/
|
||||
|
||||
// System includes
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// helper functions and utilities to work with CUDA
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_functions.h>
|
||||
#include <thread> // std::thread — C++11 standard threading
|
||||
|
||||
#include "multithreading.h"
|
||||
// CUDA runtime
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
const int N_workloads = 8;
|
||||
const int N_elements_per_workload = 100000;
|
||||
const int NumElements = 100000;
|
||||
|
||||
CUTBarrier thread_barrier;
|
||||
|
||||
void CUDART_CB myStreamCallback(cudaStream_t event, cudaError_t status, void *data);
|
||||
|
||||
struct heterogeneous_workload
|
||||
// Everything a single pipeline needs. Passed by pointer to both the
|
||||
// std::thread worker and to the stream host-function callback.
|
||||
struct Workload
|
||||
{
|
||||
int id;
|
||||
int cudaDeviceID;
|
||||
|
||||
int *h_data;
|
||||
int *d_data;
|
||||
cudaStream_t stream;
|
||||
|
||||
bool success;
|
||||
int id = 0; // arbitrary tag added to each element
|
||||
int *h_data = nullptr; // pinned host buffer (fast async copies)
|
||||
int *d_data = nullptr; // device buffer
|
||||
cudaStream_t stream = nullptr; // the single stream that orders all the work
|
||||
bool success = false; // set by the post-processing callback
|
||||
};
|
||||
|
||||
__global__ void incKernel(int *data, int N)
|
||||
// GPU kernel: increment every element by one.
|
||||
__global__ void incrementKernel(int *data, int n)
|
||||
{
|
||||
int i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i < N)
|
||||
data[i]++;
|
||||
if (i < n) {
|
||||
data[i] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
CUT_THREADPROC launch(void *void_arg)
|
||||
// Forward declaration: postprocess is defined below but called from preprocess.
|
||||
void CUDART_CB postprocess(void *arg);
|
||||
|
||||
// Worker thread: CPU pre-processing + full GPU pipeline enqueue.
|
||||
//
|
||||
// std::thread passes arguments with their actual types, so no void* casting
|
||||
// is needed. This thread fills the input buffer, then enqueues the full GPU
|
||||
// pipeline (H2D, kernel, D2H, host func) onto the workload's stream.
|
||||
void preprocess(Workload *workload)
|
||||
{
|
||||
heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;
|
||||
|
||||
// Select GPU for this CPU thread
|
||||
checkCudaErrors(cudaSetDevice(workload->cudaDeviceID));
|
||||
|
||||
// Allocate Resources
|
||||
checkCudaErrors(cudaStreamCreate(&workload->stream));
|
||||
checkCudaErrors(cudaMalloc(&workload->d_data, N_elements_per_workload * sizeof(int)));
|
||||
checkCudaErrors(cudaHostAlloc(&workload->h_data, N_elements_per_workload * sizeof(int), cudaHostAllocPortable));
|
||||
|
||||
// CPU thread generates data
|
||||
for (int i = 0; i < N_elements_per_workload; ++i) {
|
||||
// Stage 1: CPU pre-processing ----------------------------------
|
||||
printf("[thread] Stage 1: CPU pre-processing on a worker thread...\n");
|
||||
for (int i = 0; i < NumElements; ++i) {
|
||||
workload->h_data[i] = workload->id + i;
|
||||
}
|
||||
printf("[thread] Filled %d elements. First 3 inputs: %d, %d, %d\n\n",
|
||||
NumElements, workload->h_data[0], workload->h_data[1], workload->h_data[2]);
|
||||
|
||||
// Schedule work for GPU in CUDA stream without blocking the CPU thread
|
||||
// Note: Dedicated streams enable concurrent execution of workloads on the GPU
|
||||
dim3 block(512);
|
||||
dim3 grid((N_elements_per_workload + block.x - 1) / block.x);
|
||||
// Stage 2: enqueue GPU work from this thread -------------------
|
||||
const size_t bytes = NumElements * sizeof(int);
|
||||
const int threadsPerBlock = 256;
|
||||
const int blocks = (NumElements + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(workload->d_data,
|
||||
workload->h_data,
|
||||
N_elements_per_workload * sizeof(int),
|
||||
cudaMemcpyHostToDevice,
|
||||
workload->stream));
|
||||
incKernel<<<grid, block, 0, workload->stream>>>(workload->d_data, N_elements_per_workload);
|
||||
checkCudaErrors(cudaMemcpyAsync(workload->h_data,
|
||||
workload->d_data,
|
||||
N_elements_per_workload * sizeof(int),
|
||||
cudaMemcpyDeviceToHost,
|
||||
workload->stream));
|
||||
printf("[thread] Stage 2: enqueuing H2D copy, kernel, D2H copy on the stream\n");
|
||||
cudaMemcpyAsync(workload->d_data, workload->h_data, bytes, cudaMemcpyHostToDevice, workload->stream);
|
||||
incrementKernel<<<blocks, threadsPerBlock, 0, workload->stream>>>(workload->d_data, NumElements);
|
||||
cudaMemcpyAsync(workload->h_data, workload->d_data, bytes, cudaMemcpyDeviceToHost, workload->stream);
|
||||
|
||||
// New in CUDA 5.0: Add a CPU callback which is called once all currently
|
||||
// pending operations in the CUDA stream have finished
|
||||
checkCudaErrors(cudaStreamAddCallback(workload->stream, myStreamCallback, workload, 0));
|
||||
|
||||
CUT_THREADEND;
|
||||
// CPU thread end of life, GPU continues to process data...
|
||||
// Stage 3: schedule CPU post-processing on the stream -----------
|
||||
printf("[thread] Registering post-processing callback with cudaLaunchHostFunc\n\n");
|
||||
cudaLaunchHostFunc(workload->stream, postprocess, workload);
|
||||
}
|
||||
|
||||
CUT_THREADPROC postprocess(void *void_arg)
|
||||
// Stage 3: CPU post-processing, scheduled on the stream via cudaLaunchHostFunc.
|
||||
//
|
||||
// CUDA invokes this automatically once the H2D copy, the kernel, and the D2H
|
||||
// copy that precede it on the stream have all finished. Every element should
|
||||
// now equal its original value plus one (from the kernel).
|
||||
//
|
||||
// Reminder: DO NOT call any CUDA API from inside this function.
|
||||
void CUDART_CB postprocess(void *arg)
|
||||
{
|
||||
heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;
|
||||
// ... GPU is done with processing, continue on new CPU thread...
|
||||
// recover the typed pointer from the callback's void* arg
|
||||
auto *workload = static_cast<Workload *>(arg);
|
||||
|
||||
// Select GPU for this CPU thread
|
||||
checkCudaErrors(cudaSetDevice(workload->cudaDeviceID));
|
||||
// Stage 3: CPU post-processing — runs automatically when all stream work above is done
|
||||
printf("[host func] Stage 3: callback fired automatically - the GPU work is done!\n");
|
||||
printf("[host func] First 3 results: %d, %d, %d (each input +1)\n",
|
||||
workload->h_data[0], workload->h_data[1], workload->h_data[2]);
|
||||
|
||||
// CPU thread consumes results from GPU
|
||||
workload->success = true;
|
||||
|
||||
for (int i = 0; i < N_workloads; ++i) {
|
||||
workload->success &= workload->h_data[i] == i + workload->id + 1;
|
||||
}
|
||||
|
||||
// Free Resources
|
||||
checkCudaErrors(cudaFree(workload->d_data));
|
||||
checkCudaErrors(cudaFreeHost(workload->h_data));
|
||||
checkCudaErrors(cudaStreamDestroy(workload->stream));
|
||||
|
||||
// Signal the end of the heterogeneous workload to main thread
|
||||
cutIncrementBarrier(&thread_barrier);
|
||||
|
||||
CUT_THREADEND;
|
||||
}
|
||||
|
||||
void CUDART_CB myStreamCallback(cudaStream_t stream, cudaError_t status, void *data)
|
||||
{
|
||||
// Check status of GPU after stream operations are done
|
||||
checkCudaErrors(status);
|
||||
|
||||
// Spawn new CPU worker thread and continue processing on the CPU
|
||||
cutStartThread(postprocess, data);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int N_gpus, max_gpus = 0;
|
||||
int gpuInfo[32]; // assume a maximum of 32 GPUs in a system configuration
|
||||
|
||||
printf("Starting simpleCallback\n");
|
||||
|
||||
checkCudaErrors(cudaGetDeviceCount(&N_gpus));
|
||||
printf("Found %d CUDA capable GPUs\n", N_gpus);
|
||||
|
||||
if (N_gpus > 32) {
|
||||
printf("simpleCallback only supports 32 GPU(s)\n");
|
||||
}
|
||||
|
||||
for (int devid = 0; devid < N_gpus; devid++) {
|
||||
int SMversion;
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaSetDevice(devid);
|
||||
cudaGetDeviceProperties(&deviceProp, devid);
|
||||
SMversion = deviceProp.major << 4 + deviceProp.minor;
|
||||
printf("GPU[%d] %s supports SM %d.%d", devid, deviceProp.name, deviceProp.major, deviceProp.minor);
|
||||
printf(", %s GPU Callback Functions\n", (SMversion >= 0x11) ? "capable" : "NOT capable");
|
||||
|
||||
if (SMversion >= 0x11) {
|
||||
gpuInfo[max_gpus++] = devid;
|
||||
bool verify = true;
|
||||
for (int i = 0; i < NumElements; ++i) {
|
||||
if (workload->h_data[i] != workload->id + i + 1) {
|
||||
verify = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d GPUs available to run Callback Functions\n", max_gpus);
|
||||
|
||||
heterogeneous_workload *workloads;
|
||||
workloads = (heterogeneous_workload *)malloc(N_workloads * sizeof(heterogeneous_workload));
|
||||
;
|
||||
thread_barrier = cutCreateBarrier(N_workloads);
|
||||
|
||||
// Main thread spawns a CPU worker thread for each heterogeneous workload
|
||||
printf("Starting %d heterogeneous computing workloads\n", N_workloads);
|
||||
|
||||
for (int i = 0; i < N_workloads; ++i) {
|
||||
workloads[i].id = i;
|
||||
workloads[i].cudaDeviceID = gpuInfo[i % max_gpus]; // i % N_gpus;
|
||||
|
||||
cutStartThread(launch, &workloads[i]);
|
||||
}
|
||||
|
||||
// Sleep until all workloads have finished
|
||||
cutWaitForBarrier(&thread_barrier);
|
||||
printf("Total of %d workloads finished:\n", N_workloads);
|
||||
|
||||
bool success = true;
|
||||
|
||||
for (int i = 0; i < N_workloads; ++i) {
|
||||
success &= workloads[i].success;
|
||||
}
|
||||
|
||||
printf("%s\n", success ? "Success" : "Failure");
|
||||
|
||||
free(workloads);
|
||||
|
||||
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
workload->success = verify;
|
||||
printf("[host func] Verified all %d results: %s\n", NumElements, verify ? "PASS" : "MISMATCH");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("=====================================================\n");
|
||||
printf(" simpleCallback: CPU -> GPU -> CPU pipeline demo\n");
|
||||
printf("=====================================================\n");
|
||||
|
||||
// Use the first available CUDA device.
|
||||
int devID = 0;
|
||||
cudaSetDevice(devID);
|
||||
|
||||
int major = 0, minor = 0, smCount = 0;
|
||||
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devID);
|
||||
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, devID);
|
||||
cudaDeviceGetAttribute(&smCount, cudaDevAttrMultiProcessorCount, devID);
|
||||
printf("Using GPU %d: compute capability %d.%d, %d SMs\n", devID, major, minor, smCount);
|
||||
|
||||
// Allocate resources
|
||||
Workload workload;
|
||||
workload.id = 42; // Any number can be used
|
||||
|
||||
// Pinned (page-locked) host memory enables true asynchronous copies.
|
||||
const size_t bytes = NumElements * sizeof(int);
|
||||
cudaMallocHost(&workload.h_data, bytes);
|
||||
cudaMalloc(&workload.d_data, bytes);
|
||||
cudaStreamCreate(&workload.stream);
|
||||
|
||||
// Launch worker thread; it fills the buffer AND enqueues the full GPU pipeline.
|
||||
std::thread worker(preprocess, &workload);
|
||||
worker.join();
|
||||
printf("[main] Worker thread joined; GPU work has been enqueued.\n");
|
||||
|
||||
// Block until the whole pipeline (including the host function) is complete.
|
||||
printf("[main] Waiting for the stream (and callback) to finish...\n\n");
|
||||
cudaStreamSynchronize(workload.stream);
|
||||
|
||||
// Clean up
|
||||
// Safe to call CUDA APIs here: we are back on the main thread, not inside
|
||||
// the host-function callback.
|
||||
cudaStreamDestroy(workload.stream);
|
||||
cudaFree(workload.d_data);
|
||||
cudaFreeHost(workload.h_data);
|
||||
|
||||
printf("\n[main] Pipeline complete. Result: %s\n", workload.success ? "SUCCESS" : "FAILURE");
|
||||
return workload.success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleCooperativeGroups LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for simpleCooperativeGroups
|
||||
add_executable(simpleCooperativeGroups simpleCooperativeGroups.cu)
|
||||
|
||||
target_compile_options(simpleCooperativeGroups PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleCooperativeGroups PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleCooperativeGroups PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/DetectCudaArch.cmake")
|
||||
|
||||
if(SAMPLE_SKIP_BUILD)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(simpleCubemapTexture LANGUAGES C CXX CUDA)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/CudaSampleCommon.cmake")
|
||||
|
||||
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()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for simpleCubemapTexture
|
||||
add_executable(simpleCubemapTexture simpleCubemapTexture.cu)
|
||||
|
||||
target_compile_options(simpleCubemapTexture PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(simpleCubemapTexture PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(simpleCubemapTexture PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
include(InstallSamples)
|
||||
setup_samples_install()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user