mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-09-12 01:06:53 +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:
parent
b7c5481c55
commit
5443602d89
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ build
|
||||
build-*/
|
||||
test-results*/
|
||||
.vs
|
||||
.vscode
|
||||
.clangd
|
||||
test
|
||||
settings.json
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@ -1,5 +1,29 @@
|
||||
## Changelog
|
||||
|
||||
### CUDA 13.4
|
||||
* Removed obsolete samples `simpleHyperQ`, `simpleOccupancy`, `mergeSort`, `simpleTemplates`, `template`, `p2pBandwidthLatencyTest`.
|
||||
* Removed redundant `_nvrtc` sample variants (`clock_nvrtc`, `matrixMul_nvrtc`, `simpleAssert_nvrtc`, `simpleAtomicIntrinsics_nvrtc`, `inlinePTX_nvrtc`, `binomialOptions_nvrtc`, `BlackScholes_nvrtc`, `quasirandomGenerator_nvrtc`); `vectorAdd_nvrtc` remains as the canonical NVRTC example.
|
||||
* Rewrote `cpp/0_Introduction/simpleMultiGPU` to replace the hand-written reduction with `cub::BlockReduce` and the SDK stopwatch timer with CUDA events, removed helper header dependencies and merged `simpleMultiGPU.h` into the `.cu` for a single-file sample, modernized the code to C++17 (`std::vector`, `constexpr`), and added a minimum two-GPU check; updated README accordingly.
|
||||
* Modernized `cpp/3_CUDA_Features/simpleCudaGraphs`: dropped `helper_cuda.h`/`cooperative_groups.h`; used `cub::BlockReduce`; migrated to the unified `cudaGraphAddNode` API; split into `simpleCudaGraphs_explicit` and `simpleCudaGraphs_capture` sharing `simpleCudaGraphs.cuh`; added a graph-reuse demo (fresh input each launch); rewrote README.
|
||||
* Rewrote `cpp/0_Introduction/simpleAtomicIntrinsics` to show race conditions by comparing non-atomic vs atomic kernels (`atomicAdd`, `atomicMax`, `atomicCAS`), removed helper header dependencies; updated README accordingly.
|
||||
* Reorganized CMake build system for all C++ samples: centralized GPU architecture detection (`DetectCudaArch.cmake`) and common build setup (`CudaSampleCommon.cmake`) into shared modules. Samples with restricted arch support now declare a `SAMPLE_DISALLOW_ARCHS` list instead of hardcoding their own arch list, and are gracefully skipped at configure time when the requested arch is unsupported.
|
||||
* Added `sm_103` to the default set of GPU architectures the C++ samples build for.
|
||||
* Added `sm_107` to the default set of GPU architectures the C++ samples build for.
|
||||
* Rewrote `cpp/0_Introduction/simpleCallback` to replace the deprecated `cudaStreamAddCallback` with `cudaLaunchHostFunc`, replaced the `multithreading.h/.cpp` helpers with standard C++ `std::thread`, and simplified to a single-GPU, single-workload C++17 example; updated README accordingly.
|
||||
* Rewrote `cpp/0_Introduction/simpleStreams` to simplify code and remove helper header dependencies; updated README accordingly.
|
||||
* Added `python/2_CoreConcepts/persistentProgramCache` to demonstrate persisting and reusing compiled CUDA artifacts with `cuda.core` and `FileStreamProgramCache`.
|
||||
* Updated `cpp/0_Introduction/clock` to replace the manual shared-memory tree reduction with `cub::BlockReduce` min-reduction, removed dependency on `helper_cuda.h` and `helper_functions.h`, and updated README.
|
||||
* Rewrote `cpp/0_Introduction/vectorAdd` to match the corresponding example in the CUDA Programming Guide. The sample now uses Unified Memory (`cudaMallocManaged`) instead of explicit `cudaMalloc`/`cudaMemcpy`.
|
||||
* Restructured the `cpp/0_Introduction/simplePrintf` README with newer style and updated sample code by removing helper dependency.
|
||||
* Updated the `cpp/0_Introduction/simpleAssert` README to newer style, simplified code for better understanding.
|
||||
* Added `cpp/3_CUDA_Features/dmabufInterop` - a Linux-only CUDA dma-buf interoperability sample demonstrating three scenarios in a single binary: (1) same-process round-trip export/import, (2) cross-process IPC via `fork()` + `SCM_RIGHTS`, and (3) cross-GPU sharing across processes (self-skips on systems without a qualifying GPU pair).
|
||||
* Removed the unnecessary `set(CMAKE_POSITION_INDEPENDENT_CODE ON)` from the C++ sample `CMakeLists.txt` files.
|
||||
* Removed `CUDA_SEPARABLE_COMPILATION ON` from C++ samples that don't require relocatable device code (kept for the CDP samples).
|
||||
* Removed 26 math library samples from `cpp/4_CUDA_Libraries/` that are maintained in the [CUDA Library Samples](https://github.com/NVIDIA/CUDALibrarySamples) repository: cuBLAS (`simpleCUBLAS`, `simpleCUBLAS_LU`, `simpleCUBLASXT`, `matrixMulCUBLAS`, `batchCUBLAS`), cuFFT (`simpleCUFFT`, `simpleCUFFT_MGPU`, `simpleCUFFT_2d_MGPU`, `simpleCUFFT_callback`), cuRAND (`MersenneTwisterGP11213`), cuSOLVER (`cuSolverDn_LinearSolver`, `cuSolverRf`, `cuSolverSp_LinearSolver`, `cuSolverSp_LowlevelCholesky`, `cuSolverSp_LowlevelQR`), cuSPARSE (`conjugateGradient`, `conjugateGradientPrecond`, `conjugateGradientUM`), NPP (`boxFilterNPP`, `cannyEdgeDetectorNPP`, `FilterBorderControlNPP`, `freeImageInteropNPP`, `histEqualizationNPP`, `watershedSegmentationNPP`), and nvJPEG (`nvJPEG`, `nvJPEG_encoder`).
|
||||
* Lifted the LLVM 14 upper bound on `cpp/7_libNVVM/cuda-c-linking`, which now builds against LLVM 15 and newer.
|
||||
* Added samples for locality domains in `cpp/3_CUDA_FEATURES/localityDomains` and `cpp/3_CUDA_FEATURES/localityDomainsDrv`.
|
||||
* Added Windows on Arm support: `cpp/5_Domain_Specific/marchingCubes` now detects the `glew32` GLEW library name used on Windows on Arm, and `cpp/7_libNVVM` searches the `nvvm/lib/arm64` directory for libNVVM.
|
||||
|
||||
### CUDA 13.3
|
||||
* Added **CUDA Tile C++** samples under `cpp/9_CUDA_Tile`.
|
||||
* Added a set of **CCCL 3.3 feature samples** under `cpp/4_CUDA_Libraries/`, each built against CCCL fetched via CPM (pinned to v3.3.3, with an optional `CCCL_SOURCE_DIR` override):
|
||||
|
||||
@ -1,33 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include(cmake/CudaSampleArchs.cmake)
|
||||
|
||||
# Default to all supported architectures if the user does not specify.
|
||||
# Gated on CUDA_SAMPLES_ARCHS_DEFAULTED rather than CMAKE_CUDA_ARCHITECTURES so this
|
||||
# decision is made once (on the first configure) and does not flip on reconfigure,
|
||||
# when CMAKE_CUDA_ARCHITECTURES is already defined from our own cached default.
|
||||
# The decision is revisited when the list itself moves, which the user can do with -D
|
||||
# on any later configure of the same build directory. Keep the list each configure
|
||||
# ends up with, so that such a change is told apart from our own cached default.
|
||||
if(NOT DEFINED CUDA_SAMPLES_ARCHS_DEFAULTED)
|
||||
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
||||
list(JOIN CUDA_SAMPLES_MASTER_ARCHS ";" _default_archs)
|
||||
set(CMAKE_CUDA_ARCHITECTURES "${_default_archs}" CACHE STRING "CUDA architectures")
|
||||
unset(_default_archs)
|
||||
set(CUDA_SAMPLES_ARCHS_DEFAULTED TRUE CACHE INTERNAL "")
|
||||
else()
|
||||
set(CUDA_SAMPLES_ARCHS_DEFAULTED FALSE CACHE INTERNAL "")
|
||||
endif()
|
||||
elseif(NOT "${CMAKE_CUDA_ARCHITECTURES}" STREQUAL "${CUDA_SAMPLES_LAST_CONFIGURED_ARCHS}")
|
||||
set(CUDA_SAMPLES_ARCHS_DEFAULTED FALSE CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
set(CUDA_SAMPLES_LAST_CONFIGURED_ARCHS "${CMAKE_CUDA_ARCHITECTURES}" CACHE INTERNAL "")
|
||||
|
||||
project(cuda-samples LANGUAGES C CXX CUDA)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_CUDA_STANDARD 17)
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED 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()
|
||||
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --extended-lambda")
|
||||
|
||||
# Add MSVC-specific flags for standard-conforming preprocessor (required for CCCL)
|
||||
if(MSVC)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=/Zc:preprocessor>)
|
||||
endif()
|
||||
|
||||
# Include installation configuration before processing samples
|
||||
include(cmake/InstallSamples.cmake)
|
||||
include(cmake/CudaSampleCommon.cmake)
|
||||
|
||||
add_subdirectory(cpp)
|
||||
|
||||
@ -1,197 +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 NV_UTIL_NPP_EXCEPTIONS_H
|
||||
#define NV_UTIL_NPP_EXCEPTIONS_H
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
/// All npp related C++ classes are put into the npp namespace.
|
||||
namespace npp
|
||||
{
|
||||
|
||||
/// Exception base class.
|
||||
/// This exception base class will be used for everything C++ throught
|
||||
/// the NPP project.
|
||||
/// The exception contains a string message, as well as data fields for a string
|
||||
/// containing the name of the file as well as the line number where the exception was thrown.
|
||||
/// The easiest way of throwing exceptions and providing filename and line number is
|
||||
/// to use one of the ASSERT macros defined for that purpose.
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \param rMessage A message with information as to why the exception was thrown.
|
||||
/// \param rFileName The name of the file where the exception was thrown.
|
||||
/// \param nLineNumber Line number in the file where the exception was thrown.
|
||||
explicit
|
||||
Exception(const std::string &rMessage = "", const std::string &rFileName = "", unsigned int nLineNumber = 0)
|
||||
: sMessage_(rMessage), sFileName_(rFileName), nLineNumber_(nLineNumber)
|
||||
{ };
|
||||
|
||||
Exception(const Exception &rException)
|
||||
: sMessage_(rException.sMessage_), sFileName_(rException.sFileName_), nLineNumber_(rException.nLineNumber_)
|
||||
{ };
|
||||
|
||||
virtual
|
||||
~Exception()
|
||||
{ };
|
||||
|
||||
/// Get the exception's message.
|
||||
const
|
||||
std::string &
|
||||
message()
|
||||
const
|
||||
{
|
||||
return sMessage_;
|
||||
}
|
||||
|
||||
/// Get the exception's file info.
|
||||
const
|
||||
std::string &
|
||||
fileName()
|
||||
const
|
||||
{
|
||||
return sFileName_;
|
||||
}
|
||||
|
||||
/// Get the exceptions's line info.
|
||||
unsigned int
|
||||
lineNumber()
|
||||
const
|
||||
{
|
||||
return nLineNumber_;
|
||||
}
|
||||
|
||||
|
||||
/// Create a clone of this exception.
|
||||
/// This creates a new Exception object on the heap. It is
|
||||
/// the responsibility of the user of this function to free this memory
|
||||
/// (delete x).
|
||||
virtual
|
||||
Exception *
|
||||
clone()
|
||||
const
|
||||
{
|
||||
return new Exception(*this);
|
||||
}
|
||||
|
||||
/// Create a single string with all the exceptions information.
|
||||
/// The virtual toString() method is used by the operator<<()
|
||||
/// so that all exceptions derived from this base-class can print
|
||||
/// their full information correctly even if a reference to their
|
||||
/// exact type is not had at the time of printing (i.e. the basic
|
||||
/// operator<<() is used).
|
||||
virtual
|
||||
std::string
|
||||
toString()
|
||||
const
|
||||
{
|
||||
std::ostringstream oOutputString;
|
||||
oOutputString << fileName() << ":" << lineNumber() << ": " << message();
|
||||
return oOutputString.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string sMessage_; ///< Message regarding the cause of the exception.
|
||||
std::string sFileName_; ///< Name of the file where the exception was thrown.
|
||||
unsigned int nLineNumber_; ///< Line number in the file where the exception was thrown
|
||||
};
|
||||
|
||||
/// Output stream inserter for Exception.
|
||||
/// \param rOutputStream The stream the exception information is written to.
|
||||
/// \param rException The exception that's being written.
|
||||
/// \return Reference to the output stream being used.
|
||||
std::ostream &
|
||||
operator << (std::ostream &rOutputStream, const Exception &rException)
|
||||
{
|
||||
rOutputStream << rException.toString();
|
||||
return rOutputStream;
|
||||
}
|
||||
|
||||
/// Basic assert macro.
|
||||
/// This macro should be used to enforce any kind of pre or post conditions.
|
||||
/// Unlike the C-runtime assert macro, this macro does not abort execution, but throws
|
||||
/// a C++ exception. The exception is automatically filled with information about the failing
|
||||
/// condition, the filename and line number where the exception was thrown.
|
||||
/// \note The macro is written in such a way that omitting a semicolon after its usage
|
||||
/// causes a compiler error. The correct way to invoke this macro is:
|
||||
/// NPP_ASSERT(n < MAX);
|
||||
#define NPP_ASSERT(C) do {if (!(C)) throw npp::Exception(#C " assertion faild!", __FILE__, __LINE__);} while(false)
|
||||
|
||||
// ASSERT macro.
|
||||
// Same functionality as the basic assert macro with the added ability to pass
|
||||
// a message M. M should be a string literal.
|
||||
// Note: Never use code inside ASSERT() that causes a side-effect ASSERT macros may get compiled
|
||||
// out in release mode.
|
||||
#define NPP_ASSERT_MSG(C, M) do {if (!(C)) throw npp::Exception(#C " assertion faild! Message: " M, __FILE__, __LINE__);} while(false)
|
||||
|
||||
#ifdef _DEBUG
|
||||
/// Basic debug assert macro.
|
||||
/// This macro is identical in every respect to NPP_ASSERT(C) but it does get compiled to a
|
||||
/// no-op in release builds. It is therefor of utmost importance to not put statements into
|
||||
/// this macro that cause side effects required for correct program execution.
|
||||
#define NPP_DEBUG_ASSERT(C) do {if (!(C)) throw npp::Exception(#C " debug assertion faild!", __FILE__, __LINE__);} while(false)
|
||||
#else
|
||||
#define NPP_DEBUG_ASSERT(C)
|
||||
#endif
|
||||
|
||||
/// ASSERT for null-pointer test.
|
||||
/// It is safe to put code with side effects into this macro. Also: This macro never
|
||||
/// gets compiled to a no-op because resource allocation may fail based on external causes not under
|
||||
/// control of a software developer.
|
||||
#define NPP_ASSERT_NOT_NULL(P) do {if ((P) == 0) throw npp::Exception(#P " not null assertion faild!", __FILE__, __LINE__);} while(false)
|
||||
|
||||
/// Macro for flagging methods as not implemented.
|
||||
/// The macro throws an exception with a message that an implementation was missing
|
||||
#define NPP_NOT_IMPLEMENTED() do {throw npp::Exception("Implementation missing!", __FILE__, __LINE__);} while(false)
|
||||
|
||||
/// Macro for checking error return code of CUDA (runtime) calls.
|
||||
/// This macro never gets disabled.
|
||||
#define NPP_CHECK_CUDA(S) do {cudaError_t eCUDAResult; \
|
||||
eCUDAResult = S; \
|
||||
if (eCUDAResult != cudaSuccess) std::cout << "NPP_CHECK_CUDA - eCUDAResult = " << eCUDAResult << std::endl; \
|
||||
NPP_ASSERT(eCUDAResult == cudaSuccess);} while (false)
|
||||
|
||||
/// Macro for checking error return code for NPP calls.
|
||||
#define NPP_CHECK_NPP(S) do {NppStatus eStatusNPP; \
|
||||
eStatusNPP = S; \
|
||||
if (eStatusNPP != NPP_SUCCESS) std::cout << "NPP_CHECK_NPP - eStatusNPP = " << _cudaGetErrorEnum(eStatusNPP) << "("<< eStatusNPP << ")" << std::endl; \
|
||||
NPP_ASSERT(eStatusNPP == NPP_SUCCESS);} while (false)
|
||||
|
||||
/// Macro for checking error return codes from cuFFT calls.
|
||||
#define NPP_CHECK_CUFFT(S) do {cufftResult eCUFFTResult; \
|
||||
eCUFFTResult = S; \
|
||||
if (eCUFFTResult != NPP_SUCCESS) std::cout << "NPP_CHECK_CUFFT - eCUFFTResult = " << eCUFFTResult << std::endl; \
|
||||
NPP_ASSERT(eCUFFTResult == CUFFT_SUCCESS);} while (false)
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_EXCEPTIONS_H
|
||||
@ -1,155 +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 NV_UTIL_NPP_IMAGE_H
|
||||
#define NV_UTIL_NPP_IMAGE_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace npp
|
||||
{
|
||||
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
struct Size
|
||||
{
|
||||
unsigned int nWidth;
|
||||
unsigned int nHeight;
|
||||
|
||||
Size() : nWidth(0), nHeight(0)
|
||||
{ };
|
||||
|
||||
Size(unsigned int nWidthNew, unsigned nHeightNew) : nWidth(nWidthNew), nHeight(nHeightNew)
|
||||
{ };
|
||||
|
||||
Size(const Size &rSize) : nWidth(rSize.nWidth), nHeight(rSize.nHeight)
|
||||
{ };
|
||||
|
||||
Size &
|
||||
operator= (const Size &rSize)
|
||||
{
|
||||
if (&rSize == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
nWidth = rSize.nWidth;
|
||||
nHeight = rSize.nHeight;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
swap(Size &rSize)
|
||||
{
|
||||
unsigned int nTemp;
|
||||
nTemp = nWidth;
|
||||
nWidth = rSize.nWidth;
|
||||
rSize.nWidth = nTemp;
|
||||
|
||||
nTemp = nHeight;
|
||||
nHeight = rSize.nHeight;
|
||||
rSize.nHeight = nTemp;
|
||||
}
|
||||
};
|
||||
|
||||
Image()
|
||||
{ };
|
||||
|
||||
Image(unsigned int nWidth, unsigned int nHeight) : oSize_(nWidth, nHeight)
|
||||
{ };
|
||||
|
||||
Image(const Image::Size &rSize) : oSize_(rSize)
|
||||
{ };
|
||||
|
||||
Image(const Image &rImage) : oSize_(rImage.oSize_)
|
||||
{ };
|
||||
|
||||
virtual
|
||||
~Image()
|
||||
{ };
|
||||
|
||||
Image &
|
||||
operator= (const Image &rImage)
|
||||
{
|
||||
if (&rImage == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
oSize_ = rImage.oSize_;
|
||||
return *this;
|
||||
};
|
||||
|
||||
unsigned int
|
||||
width()
|
||||
const
|
||||
{
|
||||
return oSize_.nWidth;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
height()
|
||||
const
|
||||
{
|
||||
return oSize_.nHeight;
|
||||
}
|
||||
|
||||
Size
|
||||
size()
|
||||
const
|
||||
{
|
||||
return oSize_;
|
||||
}
|
||||
|
||||
void
|
||||
swap(Image &rImage)
|
||||
{
|
||||
oSize_.swap(rImage.oSize_);
|
||||
}
|
||||
|
||||
private:
|
||||
Size oSize_;
|
||||
};
|
||||
|
||||
bool
|
||||
operator== (const Image::Size &rFirst, const Image::Size &rSecond)
|
||||
{
|
||||
return rFirst.nWidth == rSecond.nWidth && rFirst.nHeight == rSecond.nHeight;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!= (const Image::Size &rFirst, const Image::Size &rSecond)
|
||||
{
|
||||
return rFirst.nWidth != rSecond.nWidth || rFirst.nHeight != rSecond.nHeight;
|
||||
}
|
||||
|
||||
} // npp namespace
|
||||
|
||||
|
||||
#endif // NV_UTIL_NPP_IMAGE_H
|
||||
@ -1,80 +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 NV_UTIL_NPP_IMAGE_ALLOCATORS_CPU_H
|
||||
#define NV_UTIL_NPP_IMAGE_ALLOCATORS_CPU_H
|
||||
|
||||
#include "Exceptions.h"
|
||||
|
||||
namespace npp
|
||||
{
|
||||
|
||||
template <typename D, size_t N>
|
||||
class ImageAllocatorCPU
|
||||
{
|
||||
public:
|
||||
static
|
||||
D *
|
||||
Malloc2D(unsigned int nWidth, unsigned int nHeight, unsigned int *pPitch)
|
||||
{
|
||||
NPP_ASSERT(nWidth * nHeight > 0);
|
||||
|
||||
D *pResult = new D[nWidth * N * nHeight];
|
||||
*pPitch = nWidth * sizeof(D) * N;
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free2D(D *pPixels)
|
||||
{
|
||||
delete[] pPixels;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy2D(D *pDst, size_t nDstPitch, const D *pSrc, size_t nSrcPitch, size_t nWidth, size_t nHeight)
|
||||
{
|
||||
const void *pSrcLine = pSrc;
|
||||
void *pDstLine = pDst;
|
||||
|
||||
for (size_t iLine = 0; iLine < nHeight; ++iLine)
|
||||
{
|
||||
// copy one line worth of data
|
||||
memcpy(pDst, pSrc, nWidth * N * sizeof(D));
|
||||
// move data pointers to next line
|
||||
pDst += nDstPitch;
|
||||
pSrc += nSrcPitch;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_IMAGE_ALLOCATORS_CPU_H
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,149 +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 NV_UTIL_NPP_IMAGE_IO_H
|
||||
#define NV_UTIL_NPP_IMAGE_IO_H
|
||||
|
||||
#include "ImagesCPU.h"
|
||||
#include "ImagesNPP.h"
|
||||
|
||||
#include "FreeImage.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <string>
|
||||
#include "string.h"
|
||||
|
||||
|
||||
// Error handler for FreeImage library.
|
||||
// In case this handler is invoked, it throws an NPP exception.
|
||||
void
|
||||
FreeImageErrorHandler(FREE_IMAGE_FORMAT oFif, const char *zMessage)
|
||||
{
|
||||
throw npp::Exception(zMessage);
|
||||
}
|
||||
|
||||
namespace npp
|
||||
{
|
||||
// Load a gray-scale image from disk.
|
||||
void
|
||||
loadImage(const std::string &rFileName, ImageCPU_8u_C1 &rImage)
|
||||
{
|
||||
// set your own FreeImage error handler
|
||||
FreeImage_SetOutputMessage(FreeImageErrorHandler);
|
||||
|
||||
FREE_IMAGE_FORMAT eFormat = FreeImage_GetFileType(rFileName.c_str());
|
||||
|
||||
// no signature? try to guess the file format from the file extension
|
||||
if (eFormat == FIF_UNKNOWN)
|
||||
{
|
||||
eFormat = FreeImage_GetFIFFromFilename(rFileName.c_str());
|
||||
}
|
||||
|
||||
NPP_ASSERT(eFormat != FIF_UNKNOWN);
|
||||
// check that the plugin has reading capabilities ...
|
||||
FIBITMAP *pBitmap;
|
||||
|
||||
if (FreeImage_FIFSupportsReading(eFormat))
|
||||
{
|
||||
pBitmap = FreeImage_Load(eFormat, rFileName.c_str());
|
||||
}
|
||||
|
||||
NPP_ASSERT(pBitmap != 0);
|
||||
// make sure this is an 8-bit single channel image
|
||||
NPP_ASSERT(FreeImage_GetColorType(pBitmap) == FIC_MINISBLACK);
|
||||
NPP_ASSERT(FreeImage_GetBPP(pBitmap) == 8);
|
||||
|
||||
// create an ImageCPU to receive the loaded image data
|
||||
ImageCPU_8u_C1 oImage(FreeImage_GetWidth(pBitmap), FreeImage_GetHeight(pBitmap));
|
||||
|
||||
// Copy the FreeImage data into the new ImageCPU
|
||||
unsigned int nSrcPitch = FreeImage_GetPitch(pBitmap);
|
||||
const Npp8u *pSrcLine = FreeImage_GetBits(pBitmap) + nSrcPitch * (FreeImage_GetHeight(pBitmap) -1);
|
||||
Npp8u *pDstLine = oImage.data();
|
||||
unsigned int nDstPitch = oImage.pitch();
|
||||
|
||||
for (size_t iLine = 0; iLine < oImage.height(); ++iLine)
|
||||
{
|
||||
memcpy(pDstLine, pSrcLine, oImage.width() * sizeof(Npp8u));
|
||||
pSrcLine -= nSrcPitch;
|
||||
pDstLine += nDstPitch;
|
||||
}
|
||||
|
||||
// swap the user given image with our result image, effecively
|
||||
// moving our newly loaded image data into the user provided shell
|
||||
oImage.swap(rImage);
|
||||
}
|
||||
|
||||
// Save an gray-scale image to disk.
|
||||
void
|
||||
saveImage(const std::string &rFileName, const ImageCPU_8u_C1 &rImage)
|
||||
{
|
||||
// create the result image storage using FreeImage so we can easily
|
||||
// save
|
||||
FIBITMAP *pResultBitmap = FreeImage_Allocate(rImage.width(), rImage.height(), 8 /* bits per pixel */);
|
||||
NPP_ASSERT_NOT_NULL(pResultBitmap);
|
||||
unsigned int nDstPitch = FreeImage_GetPitch(pResultBitmap);
|
||||
Npp8u *pDstLine = FreeImage_GetBits(pResultBitmap) + nDstPitch * (rImage.height()-1);
|
||||
const Npp8u *pSrcLine = rImage.data();
|
||||
unsigned int nSrcPitch = rImage.pitch();
|
||||
|
||||
for (size_t iLine = 0; iLine < rImage.height(); ++iLine)
|
||||
{
|
||||
memcpy(pDstLine, pSrcLine, rImage.width() * sizeof(Npp8u));
|
||||
pSrcLine += nSrcPitch;
|
||||
pDstLine -= nDstPitch;
|
||||
}
|
||||
|
||||
// now save the result image
|
||||
bool bSuccess;
|
||||
bSuccess = FreeImage_Save(FIF_PGM, pResultBitmap, rFileName.c_str(), 0) == TRUE;
|
||||
NPP_ASSERT_MSG(bSuccess, "Failed to save result image.");
|
||||
}
|
||||
|
||||
// Load a gray-scale image from disk.
|
||||
void
|
||||
loadImage(const std::string &rFileName, ImageNPP_8u_C1 &rImage)
|
||||
{
|
||||
ImageCPU_8u_C1 oImage;
|
||||
loadImage(rFileName, oImage);
|
||||
ImageNPP_8u_C1 oResult(oImage);
|
||||
rImage.swap(oResult);
|
||||
}
|
||||
|
||||
// Save an gray-scale image to disk.
|
||||
void
|
||||
saveImage(const std::string &rFileName, const ImageNPP_8u_C1 &rImage)
|
||||
{
|
||||
ImageCPU_8u_C1 oHostImage(rImage.size());
|
||||
// copy the device result data
|
||||
rImage.copyTo(oHostImage.data(), oHostImage.pitch());
|
||||
saveImage(rFileName, oHostImage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // NV_UTIL_NPP_IMAGE_IO_H
|
||||
@ -1,171 +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 NV_UTIL_NPP_IMAGE_PACKED_H
|
||||
#define NV_UTIL_NPP_IMAGE_PACKED_H
|
||||
|
||||
#include "Image.h"
|
||||
#include "Pixel.h"
|
||||
|
||||
namespace npp
|
||||
{
|
||||
template<typename D, size_t N, class A>
|
||||
class ImagePacked: public npp::Image
|
||||
{
|
||||
public:
|
||||
typedef npp::Pixel<D, N> tPixel;
|
||||
typedef D tData;
|
||||
static const size_t gnChannels = N;
|
||||
typedef npp::Image::Size tSize;
|
||||
|
||||
ImagePacked(): aPixels_(0)
|
||||
, nPitch_(0)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImagePacked(unsigned int nWidth, unsigned int nHeight): Image(nWidth, nHeight)
|
||||
, aPixels_(0)
|
||||
, nPitch_(0)
|
||||
{
|
||||
aPixels_ = A::Malloc2D(width(), height(), &nPitch_);
|
||||
}
|
||||
|
||||
ImagePacked(unsigned int nWidth, unsigned int nHeight, bool bTight): Image(nWidth, nHeight)
|
||||
, aPixels_(0)
|
||||
, nPitch_(0)
|
||||
{
|
||||
aPixels_ = A::Malloc2D(width(), height(), &nPitch_, bTight);
|
||||
}
|
||||
|
||||
ImagePacked(const tSize &rSize): Image(rSize)
|
||||
, aPixels_(0)
|
||||
, nPitch_(0)
|
||||
{
|
||||
aPixels_ = A::Malloc2D(width(), height(), &nPitch_);
|
||||
}
|
||||
|
||||
ImagePacked(const ImagePacked<D, N, A> &rImage): Image(rImage)
|
||||
, aPixels_(0)
|
||||
, nPitch_(rImage.pitch())
|
||||
{
|
||||
aPixels_ = A::Malloc2D(width(), height(), &nPitch_);
|
||||
A::Copy2D(aPixels_, nPitch_, rImage.pixels(), rImage.pitch(), width(), height());
|
||||
}
|
||||
|
||||
virtual
|
||||
~ImagePacked()
|
||||
{
|
||||
A::Free2D(aPixels_);
|
||||
}
|
||||
|
||||
ImagePacked &
|
||||
operator= (const ImagePacked<D, N, A> &rImage)
|
||||
{
|
||||
// in case of self-assignment
|
||||
if (&rImage == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
A::Free2D(aPixels_);
|
||||
aPixels_ = 0;
|
||||
nPitch_ = 0;
|
||||
|
||||
// assign parent class's data fields (width, height)
|
||||
Image::operator =(rImage);
|
||||
|
||||
aPixels_ = A::Malloc2D(width(), height(), &nPitch_);
|
||||
A::Copy2D(aPixels_, nPitch_, rImage.data(), rImage.pitch(), width(), height());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
pitch()
|
||||
const
|
||||
{
|
||||
return nPitch_;
|
||||
}
|
||||
|
||||
/// Get a pointer to the pixel array.
|
||||
/// The result pointer can be offset to pixel at position (x, y) and
|
||||
/// even negative offsets are allowed.
|
||||
/// \param nX Horizontal pointer/array offset.
|
||||
/// \param nY Vertical pointer/array offset.
|
||||
/// \return Pointer to the pixel array (or first pixel in array with coordinates (nX, nY).
|
||||
tPixel *
|
||||
pixels(int nX = 0, int nY = 0)
|
||||
{
|
||||
return reinterpret_cast<tPixel *>(reinterpret_cast<unsigned char *>(aPixels_) + nY * pitch() + nX * gnChannels * sizeof(D));
|
||||
}
|
||||
|
||||
const
|
||||
tPixel *
|
||||
pixels(int nX = 0, int nY = 0)
|
||||
const
|
||||
{
|
||||
return reinterpret_cast<const tPixel *>(reinterpret_cast<unsigned char *>(aPixels_) + nY * pitch() + nX * gnChannels * sizeof(D));
|
||||
}
|
||||
|
||||
D *
|
||||
data(int nX = 0, int nY = 0)
|
||||
{
|
||||
return reinterpret_cast<D *>(pixels(nX, nY));
|
||||
}
|
||||
|
||||
const
|
||||
D *
|
||||
data(int nX = 0, int nY = 0)
|
||||
const
|
||||
{
|
||||
return reinterpret_cast<const D *>(pixels(nX, nY));
|
||||
}
|
||||
|
||||
void
|
||||
swap(ImagePacked<D, N, A> &rImage)
|
||||
{
|
||||
Image::swap(rImage);
|
||||
|
||||
tData *aTemp = aPixels_;
|
||||
aPixels_ = rImage.aPixels_;
|
||||
rImage.aPixels_ = aTemp;
|
||||
|
||||
unsigned int nTemp = nPitch_;
|
||||
nPitch_ = rImage.nPitch_;
|
||||
rImage.nPitch_ = nTemp;
|
||||
}
|
||||
|
||||
private:
|
||||
D *aPixels_;
|
||||
unsigned int nPitch_;
|
||||
};
|
||||
|
||||
} // npp namespace
|
||||
|
||||
|
||||
#endif // NV_IMAGE_IPP_H
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
#ifndef NV_UTIL_NPP_IMAGES_CPU_H
|
||||
#define NV_UTIL_NPP_IMAGES_CPU_H
|
||||
|
||||
#include "ImagePacked.h"
|
||||
|
||||
#include "ImageAllocatorsCPU.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <npp.h>
|
||||
|
||||
|
||||
namespace npp
|
||||
{
|
||||
|
||||
template<typename D, unsigned int N, class A>
|
||||
class ImageCPU: public npp::ImagePacked<D, N, A>
|
||||
{
|
||||
public:
|
||||
|
||||
ImageCPU()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageCPU(unsigned int nWidth, unsigned int nHeight): ImagePacked<D, N, A>(nWidth, nHeight)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
explicit
|
||||
ImageCPU(const npp::Image::Size &rSize): ImagePacked<D, N, A>(rSize)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageCPU(const ImageCPU<D, N, A> &rImage): Image(rImage)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
virtual
|
||||
~ImageCPU()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageCPU &
|
||||
operator= (const ImageCPU<D, N, A> &rImage)
|
||||
{
|
||||
ImagePacked<D, N, A>::operator= (rImage);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
npp::Pixel<D, N> &
|
||||
operator()(unsigned int iX, unsigned int iY)
|
||||
{
|
||||
return *ImagePacked<D, N, A>::pixels(iX, iY);
|
||||
}
|
||||
|
||||
npp::Pixel<D, N>
|
||||
operator()(unsigned int iX, unsigned int iY)
|
||||
const
|
||||
{
|
||||
return *ImagePacked<D, N, A>::pixels(iX, iY);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef ImageCPU<Npp8u, 1, npp::ImageAllocatorCPU<Npp8u, 1> > ImageCPU_8u_C1;
|
||||
typedef ImageCPU<Npp8u, 2, npp::ImageAllocatorCPU<Npp8u, 2> > ImageCPU_8u_C2;
|
||||
typedef ImageCPU<Npp8u, 3, npp::ImageAllocatorCPU<Npp8u, 3> > ImageCPU_8u_C3;
|
||||
typedef ImageCPU<Npp8u, 4, npp::ImageAllocatorCPU<Npp8u, 4> > ImageCPU_8u_C4;
|
||||
|
||||
typedef ImageCPU<Npp16u, 1, npp::ImageAllocatorCPU<Npp16u, 1> > ImageCPU_16u_C1;
|
||||
typedef ImageCPU<Npp16u, 3, npp::ImageAllocatorCPU<Npp16u, 3> > ImageCPU_16u_C3;
|
||||
typedef ImageCPU<Npp16u, 4, npp::ImageAllocatorCPU<Npp16u, 4> > ImageCPU_16u_C4;
|
||||
|
||||
typedef ImageCPU<Npp16s, 1, npp::ImageAllocatorCPU<Npp16s, 1> > ImageCPU_16s_C1;
|
||||
typedef ImageCPU<Npp16s, 3, npp::ImageAllocatorCPU<Npp16s, 3> > ImageCPU_16s_C3;
|
||||
typedef ImageCPU<Npp16s, 4, npp::ImageAllocatorCPU<Npp16s, 4> > ImageCPU_16s_C4;
|
||||
|
||||
typedef ImageCPU<Npp32s, 1, npp::ImageAllocatorCPU<Npp32s, 1> > ImageCPU_32s_C1;
|
||||
typedef ImageCPU<Npp32s, 3, npp::ImageAllocatorCPU<Npp32s, 3> > ImageCPU_32s_C3;
|
||||
typedef ImageCPU<Npp32s, 4, npp::ImageAllocatorCPU<Npp32s, 4> > ImageCPU_32s_C4;
|
||||
|
||||
typedef ImageCPU<Npp32f, 1, npp::ImageAllocatorCPU<Npp32f, 1> > ImageCPU_32f_C1;
|
||||
typedef ImageCPU<Npp32f, 3, npp::ImageAllocatorCPU<Npp32f, 3> > ImageCPU_32f_C3;
|
||||
typedef ImageCPU<Npp32f, 4, npp::ImageAllocatorCPU<Npp32f, 4> > ImageCPU_32f_C4;
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_IMAGE_IPP_H
|
||||
@ -1,149 +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 NV_UTIL_NPP_IMAGES_NPP_H
|
||||
#define NV_UTIL_NPP_IMAGES_NPP_H
|
||||
|
||||
#include "Exceptions.h"
|
||||
#include "ImagePacked.h"
|
||||
|
||||
#include "ImageAllocatorsNPP.h"
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
namespace npp
|
||||
{
|
||||
// forward declaration
|
||||
template<typename D, unsigned int N, class A> class ImageCPU;
|
||||
|
||||
template<typename D, unsigned int N>
|
||||
class ImageNPP: public npp::ImagePacked<D, N, npp::ImageAllocator<D, N> >
|
||||
{
|
||||
public:
|
||||
ImageNPP()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageNPP(unsigned int nWidth, unsigned int nHeight, bool bTight = false): ImagePacked<D, N, npp::ImageAllocator<D, N> >(nWidth, nHeight, bTight)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageNPP(const npp::Image::Size &rSize): ImagePacked<D, N, npp::ImageAllocator<D, N> >(rSize)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageNPP(const ImageNPP<D, N> &rImage): Image(rImage)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
template<class X>
|
||||
explicit
|
||||
ImageNPP(const ImageCPU<D, N, X> &rImage, bool bTight = false): ImagePacked<D, N, npp::ImageAllocator<D, N> >(rImage.width(), rImage.height(), bTight)
|
||||
{
|
||||
npp::ImageAllocator<D, N>::HostToDeviceCopy2D(ImagePacked<D, N, npp::ImageAllocator<D, N> >::data(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::pitch(),
|
||||
rImage.data(),
|
||||
rImage.pitch(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::width(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::height());
|
||||
}
|
||||
|
||||
virtual
|
||||
~ImageNPP()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
ImageNPP &
|
||||
operator= (const ImageNPP<D, N> &rImage)
|
||||
{
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::operator= (rImage);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
copyTo(D *pData, unsigned int nPitch)
|
||||
const
|
||||
{
|
||||
NPP_ASSERT((ImagePacked<D, N, npp::ImageAllocator<D, N> >::width() * sizeof(npp::Pixel<D, N>) <= nPitch));
|
||||
npp::ImageAllocator<D, N>::DeviceToHostCopy2D(pData,
|
||||
nPitch,
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::data(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::pitch(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::width(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::height());
|
||||
}
|
||||
|
||||
void
|
||||
copyFrom(D *pData, unsigned int nPitch)
|
||||
{
|
||||
NPP_ASSERT((ImagePacked<D, N, npp::ImageAllocator<D, N> >::width() * sizeof(npp::Pixel<D, N>) <= nPitch));
|
||||
npp::ImageAllocator<D, N>::HostToDeviceCopy2D(ImagePacked<D, N, npp::ImageAllocator<D, N> >::data(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::pitch(),
|
||||
pData,
|
||||
nPitch,
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::width(),
|
||||
ImagePacked<D, N, npp::ImageAllocator<D, N> >::height());
|
||||
}
|
||||
};
|
||||
|
||||
typedef ImageNPP<Npp8u, 1> ImageNPP_8u_C1;
|
||||
typedef ImageNPP<Npp8u, 2> ImageNPP_8u_C2;
|
||||
typedef ImageNPP<Npp8u, 3> ImageNPP_8u_C3;
|
||||
typedef ImageNPP<Npp8u, 4> ImageNPP_8u_C4;
|
||||
|
||||
typedef ImageNPP<Npp16u, 1> ImageNPP_16u_C1;
|
||||
typedef ImageNPP<Npp16u, 2> ImageNPP_16u_C2;
|
||||
typedef ImageNPP<Npp16u, 3> ImageNPP_16u_C3;
|
||||
typedef ImageNPP<Npp16u, 4> ImageNPP_16u_C4;
|
||||
|
||||
typedef ImageNPP<Npp16s, 1> ImageNPP_16s_C1;
|
||||
typedef ImageNPP<Npp16s, 3> ImageNPP_16s_C3;
|
||||
typedef ImageNPP<Npp16s, 4> ImageNPP_16s_C4;
|
||||
|
||||
typedef ImageNPP<Npp32s, 1> ImageNPP_32s_C1;
|
||||
typedef ImageNPP<Npp32s, 3> ImageNPP_32s_C3;
|
||||
typedef ImageNPP<Npp32s, 4> ImageNPP_32s_C4;
|
||||
|
||||
typedef ImageNPP<Npp32f, 1> ImageNPP_32f_C1;
|
||||
typedef ImageNPP<Npp32f, 2> ImageNPP_32f_C2;
|
||||
typedef ImageNPP<Npp32f, 3> ImageNPP_32f_C3;
|
||||
typedef ImageNPP<Npp32f, 4> ImageNPP_32f_C4;
|
||||
|
||||
typedef ImageNPP<Npp64f, 1> ImageNPP_64f_C1;
|
||||
typedef ImageNPP<Npp64f, 2> ImageNPP_64f_C2;
|
||||
typedef ImageNPP<Npp64f, 3> ImageNPP_64f_C3;
|
||||
typedef ImageNPP<Npp64f, 4> ImageNPP_64f_C4;
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_IMAGES_NPP_H
|
||||
@ -1,126 +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 NV_UTIL_PIXEL_H
|
||||
#define NV_UTIL_PIXEL_H
|
||||
|
||||
#include "Exceptions.h"
|
||||
|
||||
namespace npp
|
||||
{
|
||||
template <typename D, size_t N>
|
||||
struct Pixel
|
||||
{ };
|
||||
|
||||
template <typename D>
|
||||
struct Pixel<D, 1>
|
||||
{
|
||||
D x;
|
||||
|
||||
const D &
|
||||
operator[](size_t iChannel)
|
||||
const
|
||||
{
|
||||
NPP_ASSERT(iChannel < 1);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
|
||||
D &
|
||||
operator[](size_t iChannel)
|
||||
{
|
||||
NPP_ASSERT(iChannel < 1);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename D>
|
||||
struct Pixel<D, 2>
|
||||
{
|
||||
D x,y;
|
||||
|
||||
const D &
|
||||
operator[](size_t iChannel)
|
||||
const
|
||||
{
|
||||
NPP_ASSERT(iChannel < 2);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
|
||||
D &
|
||||
operator[](size_t iChannel)
|
||||
{
|
||||
NPP_ASSERT(iChannel < 2);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename D>
|
||||
struct Pixel<D, 3>
|
||||
{
|
||||
D x,y,z;
|
||||
|
||||
const D &
|
||||
operator[](size_t iChannel)
|
||||
const
|
||||
{
|
||||
NPP_ASSERT(iChannel < 3);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
|
||||
D &
|
||||
operator[](size_t iChannel)
|
||||
{
|
||||
NPP_ASSERT(iChannel < 3);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename D>
|
||||
struct Pixel<D, 4>
|
||||
{
|
||||
D x, y, z, w;
|
||||
|
||||
const D &
|
||||
operator[](size_t iChannel)
|
||||
const
|
||||
{
|
||||
NPP_ASSERT(iChannel < 4);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
|
||||
D &
|
||||
operator[](size_t iChannel)
|
||||
{
|
||||
NPP_ASSERT(iChannel < 4);
|
||||
return (&x)[iChannel];
|
||||
}
|
||||
};
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_PIXEL_H
|
||||
@ -1,168 +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 NV_UTIL_NPP_SIGNAL_H
|
||||
#define NV_UTIL_NPP_SIGNAL_H
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace npp
|
||||
{
|
||||
class Signal
|
||||
{
|
||||
public:
|
||||
Signal() : nSize_(0)
|
||||
{ };
|
||||
|
||||
explicit
|
||||
Signal(size_t nSize) : nSize_(nSize)
|
||||
{ };
|
||||
|
||||
Signal(const Signal &rSignal) : nSize_(rSignal.nSize_)
|
||||
{ };
|
||||
|
||||
virtual
|
||||
~Signal()
|
||||
{ }
|
||||
|
||||
Signal &
|
||||
operator= (const Signal &rSignal)
|
||||
{
|
||||
nSize_ = rSignal.nSize_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t
|
||||
size()
|
||||
const
|
||||
{
|
||||
return nSize_;
|
||||
}
|
||||
|
||||
void
|
||||
swap(Signal &rSignal)
|
||||
{
|
||||
size_t nTemp = nSize_;
|
||||
nSize_ = rSignal.nSize_;
|
||||
rSignal.nSize_ = nTemp;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
size_t nSize_;
|
||||
};
|
||||
|
||||
template<typename D, class A>
|
||||
class SignalTemplate: public Signal
|
||||
{
|
||||
public:
|
||||
typedef D tData;
|
||||
|
||||
SignalTemplate(): aValues_(0)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SignalTemplate(size_t nSize): Signal(nSize)
|
||||
, aValues_(0)
|
||||
{
|
||||
aValues_ = A::Malloc1D(size());
|
||||
}
|
||||
|
||||
SignalTemplate(const SignalTemplate<D, A> &rSignal): Signal(rSignal)
|
||||
, aValues_(0)
|
||||
{
|
||||
aValues_ = A::Malloc1D(size());
|
||||
A::Copy1D(aValues_, rSignal.values(), size());
|
||||
}
|
||||
|
||||
virtual
|
||||
~SignalTemplate()
|
||||
{
|
||||
A::Free1D(aValues_);
|
||||
}
|
||||
|
||||
SignalTemplate &
|
||||
operator= (const SignalTemplate<D, A> &rSignal)
|
||||
{
|
||||
// in case of self-assignment
|
||||
if (&rSignal == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
A::Free1D(aValues_);
|
||||
this->aPixels_ = 0;
|
||||
|
||||
// assign parent class's data fields (width, height)
|
||||
Signal::operator =(rSignal);
|
||||
|
||||
aValues_ = A::Malloc1D(size());
|
||||
A::Copy1D(aValues_, rSignal.value(), size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get a pointer to the pixel array.
|
||||
/// The result pointer can be offset to pixel at position (x, y) and
|
||||
/// even negative offsets are allowed.
|
||||
/// \param nX Horizontal pointer/array offset.
|
||||
/// \param nY Vertical pointer/array offset.
|
||||
/// \return Pointer to the pixel array (or first pixel in array with coordinates (nX, nY).
|
||||
tData *
|
||||
values(int i = 0)
|
||||
{
|
||||
return aValues_ + i;
|
||||
}
|
||||
|
||||
const
|
||||
tData *
|
||||
values(int i = 0)
|
||||
const
|
||||
{
|
||||
return aValues_ + i;
|
||||
}
|
||||
|
||||
void
|
||||
swap(SignalTemplate<D, A> &rSignal)
|
||||
{
|
||||
Signal::swap(rSignal);
|
||||
|
||||
tData *aTemp = this->aValues_;
|
||||
this->aValues_ = rSignal.aValues_;
|
||||
rSignal.aValues_ = aTemp;
|
||||
}
|
||||
|
||||
private:
|
||||
D *aValues_;
|
||||
};
|
||||
|
||||
} // npp namespace
|
||||
|
||||
|
||||
#endif // NV_UTIL_NPP_SIGNAL_H
|
||||
@ -1,66 +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 NV_UTIL_NPP_SIGNAL_ALLOCATORS_CPU_H
|
||||
#define NV_UTIL_NPP_SIGNAL_ALLOCATORS_CPU_H
|
||||
|
||||
#include "Exceptions.h"
|
||||
|
||||
namespace npp
|
||||
{
|
||||
|
||||
template <typename D>
|
||||
class SignalAllocatorCPU
|
||||
{
|
||||
public:
|
||||
static
|
||||
D *
|
||||
Malloc1D(unsigned int nSize)
|
||||
{
|
||||
return new D[nSize];;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(D *pPixels)
|
||||
{
|
||||
delete[] pPixels;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(D *pDst, const D *pSrc, size_t nSize)
|
||||
{
|
||||
memcpy(pDst, pSrc, nSize * sizeof(D));
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_SIGNAL_ALLOCATORS_CPU_H
|
||||
@ -1,684 +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 NV_UTIL_NPP_SIGNAL_ALLOCATORS_NPP_H
|
||||
#define NV_UTIL_NPP_SIGNAL_ALLOCATORS_NPP_H
|
||||
|
||||
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <npps.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
namespace npp
|
||||
{
|
||||
|
||||
template <typename D>
|
||||
class SignalAllocator
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp8u>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp8u *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp8u *pResult = nppsMalloc_8u(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp8u *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp8u *pDst, const Npp8u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp8u),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp8u *pDst, const Npp8u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp8u), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp8u *pDst, const Npp8u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp8u), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp16s>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp16s *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp16s *pResult = nppsMalloc_16s(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp16s *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp16s *pDst, const Npp16s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16s),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp16s *pDst, const Npp16s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16s), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp16s *pDst, const Npp16s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16s), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp16u>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp16u *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp16u *pResult = nppsMalloc_16u(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp16u *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp16u *pDst, const Npp16u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16u),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp16u *pDst, const Npp16u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16u), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp16u *pDst, const Npp16u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16u), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp16sc>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp16sc *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp16sc *pResult = nppsMalloc_16sc(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp16sc *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp16sc *pDst, const Npp16sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16sc),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp16sc *pDst, const Npp16sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16sc), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp16sc *pDst, const Npp16sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp16sc), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp32u>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp32u *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp32u *pResult = nppsMalloc_32u(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp32u *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp32u *pDst, const Npp32u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32u),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp32u *pDst, const Npp32u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32u), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp32u *pDst, const Npp32u *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32u), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp32s>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp32s *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp32s *pResult = nppsMalloc_32s(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp32s *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp32s *pDst, const Npp32s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32s),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp32s *pDst, const Npp32s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32s), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp32s *pDst, const Npp32s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32s), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp32sc>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp32sc *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp32sc *pResult = nppsMalloc_32sc(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp32sc *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp32sc *pDst, const Npp32sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32sc),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp32sc *pDst, const Npp32sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32sc), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp32sc *pDst, const Npp32sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32sc), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp32f>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp32f *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp32f *pResult = nppsMalloc_32f(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp32f *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp32f *pDst, const Npp32f *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32f),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp32f *pDst, const Npp32f *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32f), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp32f *pDst, const Npp32f *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32f), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp32fc>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp32fc *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp32fc *pResult = nppsMalloc_32fc(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp32fc *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp32fc *pDst, const Npp32fc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32fc),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp32fc *pDst, const Npp32fc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32fc), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp32fc *pDst, const Npp32fc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp32fc), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp64s>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp64s *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp64s *pResult = nppsMalloc_64s(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp64s *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp64s *pDst, const Npp64s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64s),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp64s *pDst, const Npp64s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64s), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp64s *pDst, const Npp64s *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64s), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp64sc>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp64sc *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp64sc *pResult = nppsMalloc_64sc(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp64sc *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp64sc *pDst, const Npp64sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64sc),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp64sc *pDst, const Npp64sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64sc), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp64sc *pDst, const Npp64sc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64sc), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp64f>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp64f *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp64f *pResult = nppsMalloc_64f(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp64f *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp64f *pDst, const Npp64f *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64f),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp64f *pDst, const Npp64f *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64f), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp64f *pDst, const Npp64f *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64f), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
class SignalAllocator<Npp64fc>
|
||||
{
|
||||
public:
|
||||
static
|
||||
Npp64fc *
|
||||
Malloc1D(size_t nSize)
|
||||
{
|
||||
Npp64fc *pResult = nppsMalloc_64fc(static_cast<int>(nSize));
|
||||
NPP_ASSERT(pResult != 0);
|
||||
|
||||
return pResult;
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Free1D(Npp64fc *pValues)
|
||||
{
|
||||
nppsFree(pValues);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
Copy1D(Npp64fc *pDst, const Npp64fc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64fc),cudaMemcpyDeviceToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
HostToDeviceCopy1D(Npp64fc *pDst, const Npp64fc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64fc), cudaMemcpyHostToDevice);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
DeviceToHostCopy1D(Npp64fc *pDst, const Npp64fc *pSrc, size_t nSize)
|
||||
{
|
||||
cudaError_t eResult;
|
||||
eResult = cudaMemcpy(pDst, pSrc, nSize * sizeof(Npp64fc), cudaMemcpyDeviceToHost);
|
||||
NPP_ASSERT(cudaSuccess == eResult);
|
||||
};
|
||||
};
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_SIGNAL_ALLOCATORS_NPP_H
|
||||
@ -1,107 +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 NV_UTIL_NPP_SIGNALS_CPU_H
|
||||
#define NV_UTIL_NPP_SIGNALS_CPU_H
|
||||
|
||||
#include "Signal.h"
|
||||
|
||||
#include "SignalAllocatorsCPU.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <npp.h>
|
||||
|
||||
|
||||
namespace npp
|
||||
{
|
||||
|
||||
template<typename D, class A>
|
||||
class SignalCPU: public npp::SignalTemplate<D, A>
|
||||
{
|
||||
public:
|
||||
typedef typename npp::SignalTemplate<D, A>::tData tData;
|
||||
|
||||
SignalCPU()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SignalCPU(size_t nSize): SignalTemplate<D, A>(nSize)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SignalCPU(const SignalCPU<D, A> &rSignal): SignalTemplate<D, A>(rSignal)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
virtual
|
||||
~SignalCPU()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SignalCPU &
|
||||
operator= (const SignalCPU<D,A> &rSignal)
|
||||
{
|
||||
SignalTemplate<D, A>::operator= (rSignal);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
tData &
|
||||
operator [](unsigned int i)
|
||||
{
|
||||
return *SignalTemplate<D, A>::values(i);
|
||||
}
|
||||
|
||||
tData
|
||||
operator [](unsigned int i)
|
||||
const
|
||||
{
|
||||
return *SignalTemplate<D, A>::values(i);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typedef SignalCPU<Npp8u, npp::SignalAllocatorCPU<Npp8u> > SignalCPU_8u;
|
||||
typedef SignalCPU<Npp32s, npp::SignalAllocatorCPU<Npp32s> > SignalCPU_32s;
|
||||
typedef SignalCPU<Npp16s, npp::SignalAllocatorCPU<Npp16s> > SignalCPU_16s;
|
||||
typedef SignalCPU<Npp16sc, npp::SignalAllocatorCPU<Npp16sc> > SignalCPU_16sc;
|
||||
typedef SignalCPU<Npp32sc, npp::SignalAllocatorCPU<Npp32sc> > SignalCPU_32sc;
|
||||
typedef SignalCPU<Npp32f, npp::SignalAllocatorCPU<Npp32f> > SignalCPU_32f;
|
||||
typedef SignalCPU<Npp32fc, npp::SignalAllocatorCPU<Npp32fc> > SignalCPU_32fc;
|
||||
typedef SignalCPU<Npp64s, npp::SignalAllocatorCPU<Npp64s> > SignalCPU_64s;
|
||||
typedef SignalCPU<Npp64sc, npp::SignalAllocatorCPU<Npp64sc> > SignalCPU_64sc;
|
||||
typedef SignalCPU<Npp64f, npp::SignalAllocatorCPU<Npp64f> > SignalCPU_64f;
|
||||
typedef SignalCPU<Npp64fc, npp::SignalAllocatorCPU<Npp64fc> > SignalCPU_64fc;
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_SIGNALS_CPU_H
|
||||
@ -1,113 +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 NV_UTIL_NPP_SIGNALS_NPP_H
|
||||
#define NV_UTIL_NPP_SIGNALS_NPP_H
|
||||
|
||||
#include "Exceptions.h"
|
||||
#include "Signal.h"
|
||||
|
||||
#include "SignalAllocatorsNPP.h"
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
namespace npp
|
||||
{
|
||||
// forward declaration
|
||||
template<typename D, class A> class SignalCPU;
|
||||
|
||||
template<typename D>
|
||||
class SignalNPP: public npp::SignalTemplate<D, npp::SignalAllocator<D> >
|
||||
{
|
||||
public:
|
||||
SignalNPP()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
explicit
|
||||
SignalNPP(size_t nSize): SignalTemplate<D, npp::SignalAllocator<D> >(nSize)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SignalNPP(const SignalNPP<D> &rSignal): SignalTemplate<D, npp::SignalAllocator<D> >(rSignal)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
template<class X>
|
||||
explicit
|
||||
SignalNPP(const SignalCPU<D, X> &rSignal): SignalTemplate<D, npp::SignalAllocator<D> >(rSignal.size())
|
||||
{
|
||||
npp::SignalAllocator<D>::HostToDeviceCopy1D(SignalTemplate<D, npp::SignalAllocator<D> >::values(),
|
||||
rSignal.values(), SignalTemplate<D, npp::SignalAllocator<D> >::size());
|
||||
}
|
||||
|
||||
virtual
|
||||
~SignalNPP()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SignalNPP &
|
||||
operator= (const SignalNPP<D> &rSignal)
|
||||
{
|
||||
SignalTemplate<D, npp::SignalAllocator<D> >::operator= (rSignal);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
copyTo(D *pValues)
|
||||
const
|
||||
{
|
||||
npp::SignalAllocator<D>::DeviceToHostCopy1D(pValues, SignalTemplate<D, npp::SignalAllocator<D> >::values(), SignalTemplate<D, npp::SignalAllocator<D> >::size());
|
||||
}
|
||||
|
||||
void
|
||||
copyFrom(D *pValues)
|
||||
{
|
||||
npp::SignalAllocator<D>::HostToDeviceCopy1D(SignalTemplate<D, npp::SignalAllocator<D> >::values(), pValues, SignalTemplate<D, npp::SignalAllocator<D> >::size());
|
||||
}
|
||||
};
|
||||
|
||||
typedef SignalNPP<Npp8u> SignalNPP_8u;
|
||||
typedef SignalNPP<Npp16s> SignalNPP_16s;
|
||||
typedef SignalNPP<Npp16sc> SignalNPP_16sc;
|
||||
typedef SignalNPP<Npp32s> SignalNPP_32s;
|
||||
typedef SignalNPP<Npp32sc> SignalNPP_32sc;
|
||||
typedef SignalNPP<Npp32f> SignalNPP_32f;
|
||||
typedef SignalNPP<Npp32fc> SignalNPP_32fc;
|
||||
typedef SignalNPP<Npp64s> SignalNPP_64s;
|
||||
typedef SignalNPP<Npp64sc> SignalNPP_64sc;
|
||||
typedef SignalNPP<Npp64f> SignalNPP_64f;
|
||||
typedef SignalNPP<Npp64fc> SignalNPP_64fc;
|
||||
|
||||
} // npp namespace
|
||||
|
||||
#endif // NV_UTIL_NPP_SIGNALS_NPP_H
|
||||
@ -674,6 +674,7 @@ inline int _ConvertSMVer2Cores(int major, int minor) {
|
||||
{0xa0, 128},
|
||||
{0xa1, 128},
|
||||
{0xa3, 128},
|
||||
{0xa7, 128},
|
||||
{0xb0, 128},
|
||||
{0xc0, 128},
|
||||
{0xc1, 128},
|
||||
@ -729,6 +730,7 @@ inline const char* _ConvertSMVer2ArchName(int major, int minor) {
|
||||
{0xa0, "Blackwell"},
|
||||
{0xa1, "Blackwell"},
|
||||
{0xa3, "Blackwell"},
|
||||
{0xa7, "Rubin"},
|
||||
{0xb0, "Blackwell"},
|
||||
{0xc0, "Blackwell"},
|
||||
{0xc1, "Blackwell"},
|
||||
|
||||
@ -119,6 +119,7 @@ inline int _ConvertSMVer2CoresDRV(int major, int minor) {
|
||||
{0xa0, 128},
|
||||
{0xa1, 128},
|
||||
{0xa3, 128},
|
||||
{0xa7, 128},
|
||||
{0xb0, 128},
|
||||
{0xc0, 128},
|
||||
{0xc1, 128},
|
||||
|
||||
@ -1143,10 +1143,16 @@ inline __host__ __device__ uint4 max(uint4 a, uint4 b)
|
||||
// - linear interpolation between a and b, based on value t in [0, 1] range
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// std::lerp (C++20) is pulled into the global namespace by GCC's <cmath>;
|
||||
// only define our own scalar overload when std::lerp is unavailable, to avoid
|
||||
// a redeclaration / ambiguous-overload clash. (The float2/3/4 overloads below
|
||||
// take distinct argument types and never conflict.)
|
||||
#if !defined(__cplusplus) || (__cplusplus < 202002L)
|
||||
inline __device__ __host__ float lerp(float a, float b, float t)
|
||||
{
|
||||
return a + t*(b-a);
|
||||
}
|
||||
#endif
|
||||
inline __device__ __host__ float2 lerp(float2 a, float2 b, float t)
|
||||
{
|
||||
return a + t*(b-a);
|
||||
|
||||
@ -185,22 +185,24 @@ int ipcCreateSocket(ipcHandle *&handle, const char *name,
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path_name[50];
|
||||
char path_name[sizeof(servaddr.sun_path)];
|
||||
|
||||
// Create unique name for the socket with path if SOCK_FOLDER is set.
|
||||
sprintf(path_name, "%s/%u", getSocketFolder().c_str(), getpid());
|
||||
int written = snprintf(path_name, sizeof(path_name), "%s/%u",
|
||||
getSocketFolder().c_str(), getpid());
|
||||
if (written < 0 || (size_t)written >= sizeof(path_name)) {
|
||||
perror("IPC failure: Cannot bind provided name to socket. Name too large");
|
||||
close(server_fd);
|
||||
delete handle;
|
||||
handle = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
unlink(path_name);
|
||||
memset(&servaddr, 0, sizeof(servaddr));
|
||||
servaddr.sun_family = AF_UNIX;
|
||||
|
||||
size_t len = strlen(path_name);
|
||||
if (len > (sizeof(servaddr.sun_path) - 1)) {
|
||||
perror("IPC failure: Cannot bind provided name to socket. Name too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(servaddr.sun_path, path_name, len);
|
||||
strncpy(servaddr.sun_path, path_name, sizeof(servaddr.sun_path) - 1);
|
||||
|
||||
if (bind(server_fd, (struct sockaddr *)&servaddr, SUN_LEN(&servaddr)) < 0) {
|
||||
perror("IPC failure: Binding socket failed");
|
||||
@ -227,10 +229,18 @@ int ipcOpenSocket(ipcHandle *&handle) {
|
||||
|
||||
memset(&cliaddr, 0, sizeof(cliaddr));
|
||||
cliaddr.sun_family = AF_UNIX;
|
||||
char temp[50];
|
||||
char temp[sizeof(cliaddr.sun_path)];
|
||||
|
||||
// Create unique name for the socket with path if SOCK_FOLDER is set.
|
||||
sprintf(temp, "%s/%u", getSocketFolder().c_str(), getpid());
|
||||
int written = snprintf(temp, sizeof(temp), "%s/%u", getSocketFolder().c_str(),
|
||||
getpid());
|
||||
if (written < 0 || (size_t)written >= sizeof(temp)) {
|
||||
perror("IPC failure: Cannot bind provided name to socket. Name too large");
|
||||
close(sock);
|
||||
delete handle;
|
||||
handle = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(cliaddr.sun_path, temp);
|
||||
if (bind(sock, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) {
|
||||
@ -366,8 +376,14 @@ int ipcSendShareableHandle(ipcHandle *handle,
|
||||
// Construct client address to send this SHareable handle to
|
||||
memset(&cliaddr, 0, sizeof(cliaddr));
|
||||
cliaddr.sun_family = AF_UNIX;
|
||||
char temp[20];
|
||||
sprintf(temp, "%s/%u", getSocketFolder().c_str(), process);
|
||||
char temp[sizeof(cliaddr.sun_path)];
|
||||
int written =
|
||||
snprintf(temp, sizeof(temp), "%s/%u", getSocketFolder().c_str(), process);
|
||||
if (written < 0 || (size_t)written >= sizeof(temp)) {
|
||||
perror("IPC failure: Cannot address client socket. Name too large");
|
||||
free(control_un.control);
|
||||
return -1;
|
||||
}
|
||||
strcpy(cliaddr.sun_path, temp);
|
||||
len = sizeof(cliaddr);
|
||||
|
||||
|
||||
81
README.md
81
README.md
@ -1,6 +1,6 @@
|
||||
# CUDA Samples
|
||||
|
||||
Samples for CUDA Developers which demonstrates features in CUDA Toolkit. This version supports [CUDA Toolkit 13.3](https://developer.nvidia.com/cuda-downloads).
|
||||
Samples for CUDA Developers which demonstrates features in CUDA Toolkit. This version supports [CUDA Toolkit 13.4](https://developer.nvidia.com/cuda-downloads).
|
||||
|
||||
## Release Notes
|
||||
|
||||
@ -28,8 +28,6 @@ Without using git the easiest way to use these samples is to download the zip fi
|
||||
|
||||
## Building CUDA Samples
|
||||
|
||||
### Building CUDA Samples
|
||||
|
||||
The CUDA Samples are built using CMake. Follow the instructions below for building on Linux, Windows, and for cross-compilation to Tegra devices.
|
||||
|
||||
### Linux
|
||||
@ -51,7 +49,27 @@ Build the samples:
|
||||
```
|
||||
make -j$(nproc)
|
||||
```
|
||||
Run the samples from their respective directories in the build folder. You can also follow this process from and subdirectory of the samples repo, or from within any individual sample.
|
||||
|
||||
By default, samples are compiled for all GPU architectures supported by this release. If you only need to target a specific GPU, you can override this to build for a single architecture and reduce build time considerably:
|
||||
```
|
||||
cmake -DCMAKE_CUDA_ARCHITECTURES=<arch> ..
|
||||
```
|
||||
Replace `<arch>` with your GPU's SM version (e.g. `90` for sm_90).
|
||||
|
||||
Run the samples from their respective directories in the build folder.
|
||||
|
||||
### Building a Single Sample
|
||||
|
||||
To build just one sample, configure CMake from within the sample's directory. You must explicitly specify a GPU architecture — standalone builds have no top-level default:
|
||||
|
||||
```bash
|
||||
cd cpp/<category>/<sampleName>
|
||||
mkdir -p build && cd build
|
||||
cmake -DCMAKE_CUDA_ARCHITECTURES=<arch> ..
|
||||
make
|
||||
```
|
||||
|
||||
Replace `<arch>` with your GPU's SM version (e.g. `90` for sm_90).
|
||||
|
||||
### Windows
|
||||
|
||||
@ -170,23 +188,66 @@ $ make -j$(nproc) --ignore-errors # or --keep-going
|
||||
|
||||
### QNX
|
||||
|
||||
Cross-compilation for QNX with CMake is supported in the CUDA 13.0 samples release and newer. An example build for
|
||||
the Tegra Thor QNX platform might look like this:
|
||||
Cross-compilation for QNX with CMake is supported in the CUDA 13.0 samples release and newer.
|
||||
|
||||
Set up the QNX SDP paths:
|
||||
|
||||
```bash
|
||||
export QNX_HOST=/path/to/qnx/host
|
||||
export QNX_TARGET=/path/to/qnx/target
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
|
||||
QNX_HOST=/path/to/qnx/host \
|
||||
QNX_TARGET=/path/to/qnx/target \
|
||||
Build the samples for the Tegra Thor QNX platform:
|
||||
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DBUILD_TEGRA=True \
|
||||
-DCMAKE_CUDA_COMPILER=/usr/local/cuda-13.3/bin/nvcc \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/toolchain-aarch64-qnx.cmake \
|
||||
-DTARGET_FS=/path/to/qnx/targetfs \
|
||||
-DCMAKE_LIBRARY_PATH=/usr/local/cuda-13.3/thor/targets/aarch64-qnx/lib/stubs/ \
|
||||
-DCMAKE_INCLUDE_PATH=/usr/local/cuda-13.3/thor/targets/aarch64-qnx/include/
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
`TARGET_FS` is the QNX target filesystem of your board. The `cudaNvSci` sample needs it, because the NvSci headers and libraries ship with the target filesystem and not with the CUDA toolkit. Without `TARGET_FS` the build reports `NvSCI not found` and skips the sample.
|
||||
|
||||
The target filesystem is part of the NVIDIA DRIVE OS QNX SDK, which registered users download from [NVONLINE](https://partners.nvidia.com). After the SDK is installed, the filesystem is in the DRIVE OS workspace: `<NV_WORKSPACE>/drive-qnx` for the standard SDK and `<NV_WORKSPACE>/drive-qnx-safety` for the safety SDK. See the [DRIVE OS documentation](https://docs.nvidia.com/drive/) for the installation guides.
|
||||
|
||||
For both QNX toolchains the build looks for `nvscibuf.h` in `<TARGET_FS>/include`, `<TARGET_FS>/../include` and `<TARGET_FS>/usr/include`, and for `libnvscibuf.so` in `<TARGET_FS>/lib-target`, `<TARGET_FS>/usr/libnvidia` and `<TARGET_FS>/usr/lib`. If your filesystem uses a different layout, set `NVSCIBUF_INCLUDE_DIR`, `NVSCISYNC_INCLUDE_DIR`, `NVSCIBUF_LIBRARY` and `NVSCISYNC_LIBRARY` on the cmake command line instead.
|
||||
|
||||
### QNX Safety (CUDA Safe toolkit)
|
||||
|
||||
Cross-compilation for **QNX Safety** uses the CUDA Safe toolkit (for example `/usr/local/cuda-13.3-safe`).
|
||||
|
||||
Set up the QNX SDP and Safe toolkit paths:
|
||||
|
||||
```bash
|
||||
export QNX_HOST=/path/to/qnx/host
|
||||
export QNX_TARGET=/path/to/qnx/target
|
||||
export CUDA_PATH=/usr/local/cuda-13.3-safe
|
||||
export PATH=$CUDA_PATH/nvvm/bin:$PATH
|
||||
```
|
||||
|
||||
Build a single sample (standalone configure from the sample directory).
|
||||
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../../../cmake/toolchains/toolchain-aarch64-qnx-safe.cmake \
|
||||
-DCMAKE_CUDA_COMPILER=$CUDA_PATH/bin/nvcc \
|
||||
-DCMAKE_CUDA_ARCHITECTURES=110 \
|
||||
-DTARGET_FS=/path/to/qnx/safety/targetfs
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
The safe toolchain sets `-safety-compat`, links shared `libcudart` and `libcuda`, and uses QNX `q++` as the CUDA host compiler (`CMAKE_CUDA_HOST_COMPILER`). Set `CMAKE_CUDA_ARCHITECTURES` to match your GPU (`87` for Orin, `110` for Thor).
|
||||
|
||||
Supported QNX Safety samples: `matrixMul`, `cudaNvSci`.
|
||||
|
||||
`TARGET_FS` is only needed for `cudaNvSci`, which links the NvSci libraries from the safety target filesystem (`<NV_WORKSPACE>/drive-qnx-safety`, part of the DRIVE OS QNX Safety SDK). `matrixMul` builds without it. The NvSci search paths are the same as for the standard QNX build above.
|
||||
|
||||
### Forward Compatibility
|
||||
|
||||
To build samples with new CUDA Toolkit(CUDA 13.0 or later) and UMD(Version 580 or later) and old KMD(Version 550 or earlier),you need to set the `CMAKE_PREFIX_PATH` for using new driver library, the command might like this:
|
||||
|
||||
1
cmake/CudaSampleArchs.cmake
Normal file
1
cmake/CudaSampleArchs.cmake
Normal file
@ -0,0 +1 @@
|
||||
set(CUDA_SAMPLES_MASTER_ARCHS 75 80 86 87 89 90 100 103 107 110 120)
|
||||
19
cmake/CudaSampleCommon.cmake
Normal file
19
cmake/CudaSampleCommon.cmake
Normal file
@ -0,0 +1,19 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
get_filename_component(_cuda_sample_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${_cuda_sample_cmake_dir}")
|
||||
list(APPEND CMAKE_MODULE_PATH "${_cuda_sample_cmake_dir}/Modules")
|
||||
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G")
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=/Zc:preprocessor>)
|
||||
endif()
|
||||
|
||||
unset(_cuda_sample_cmake_dir)
|
||||
66
cmake/DetectCudaArch.cmake
Normal file
66
cmake/DetectCudaArch.cmake
Normal file
@ -0,0 +1,66 @@
|
||||
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
||||
message(FATAL_ERROR
|
||||
"CMAKE_CUDA_ARCHITECTURES is not set.\n"
|
||||
"For standalone sample builds, specify a target architecture explicitly:\n"
|
||||
" cmake -DCMAKE_CUDA_ARCHITECTURES=<arch> <path-to-sample>\n"
|
||||
"Example: cmake -DCMAKE_CUDA_ARCHITECTURES=90 .")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/CudaSampleArchs.cmake")
|
||||
|
||||
# Master list of all CUDA architectures supported by this repo and CUDA 13.x.
|
||||
# Each sample may define SAMPLE_DISALLOW_ARCHS to exclude architectures it does not support.
|
||||
set(_master_archs ${CUDA_SAMPLES_MASTER_ARCHS})
|
||||
|
||||
# Compute this sample's effective arch list: master minus any disallowed archs.
|
||||
set(_effective_archs ${_master_archs})
|
||||
if(DEFINED SAMPLE_DISALLOW_ARCHS)
|
||||
foreach(_arch ${SAMPLE_DISALLOW_ARCHS})
|
||||
list(REMOVE_ITEM _effective_archs "${_arch}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
get_filename_component(_sample_dir "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
|
||||
|
||||
# CMAKE_CUDA_ARCHITECTURES is set by the top-level CMakeLists.txt (defaulting to the
|
||||
# full master list if the user did not specify). Build only for the intersection with
|
||||
# the effective list; skip the sample if no requested arch is supported.
|
||||
# CMake special keywords (all, all-major, native) are passed through as-is.
|
||||
set(_passthrough_values "all" "all-major" "native")
|
||||
if(CMAKE_CUDA_ARCHITECTURES IN_LIST _passthrough_values)
|
||||
set(_passthrough TRUE)
|
||||
else()
|
||||
set(_passthrough FALSE)
|
||||
endif()
|
||||
|
||||
if(NOT _passthrough)
|
||||
set(_build_archs)
|
||||
foreach(_arch ${CMAKE_CUDA_ARCHITECTURES})
|
||||
if(_arch IN_LIST _effective_archs)
|
||||
list(APPEND _build_archs "${_arch}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _build_archs)
|
||||
if(NOT CUDA_SAMPLES_ARCHS_DEFAULTED)
|
||||
message(WARNING
|
||||
"Sample in directory '${_sample_dir}' skipped: "
|
||||
"user specified '${CMAKE_CUDA_ARCHITECTURES}' but this sample only supports '${_effective_archs}'")
|
||||
endif()
|
||||
set(SAMPLE_SKIP_BUILD TRUE)
|
||||
elseif(NOT "${_build_archs}" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}" AND NOT CUDA_SAMPLES_ARCHS_DEFAULTED)
|
||||
message(WARNING
|
||||
"Sample in directory '${_sample_dir}': user requested some architecture(s) that are not supported. "
|
||||
"Only building for '${_build_archs}'")
|
||||
set(CMAKE_CUDA_ARCHITECTURES "${_build_archs}")
|
||||
else()
|
||||
set(CMAKE_CUDA_ARCHITECTURES "${_build_archs}")
|
||||
endif()
|
||||
unset(_build_archs)
|
||||
endif()
|
||||
|
||||
unset(_master_archs)
|
||||
unset(_effective_archs)
|
||||
unset(_sample_dir)
|
||||
unset(_passthrough)
|
||||
unset(_passthrough_values)
|
||||
@ -1,17 +0,0 @@
|
||||
find_path(FreeImage_INCLUDE_DIR
|
||||
NAMES freeimage.h FreeImage.h
|
||||
PATHS /usr/include /usr/local/include
|
||||
)
|
||||
|
||||
find_library(FreeImage_LIBRARY
|
||||
NAMES freeimage FreeImage
|
||||
PATHS /usr/lib /usr/local/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(FreeImage DEFAULT_MSG FreeImage_LIBRARY FreeImage_INCLUDE_DIR)
|
||||
|
||||
if(FreeImage_FOUND)
|
||||
set(FreeImage_LIBRARIES ${FreeImage_LIBRARY})
|
||||
set(FreeImage_INCLUDE_DIRS ${FreeImage_INCLUDE_DIR})
|
||||
endif()
|
||||
136
cmake/toolchains/toolchain-aarch64-qnx-safe.cmake
Normal file
136
cmake/toolchains/toolchain-aarch64-qnx-safe.cmake
Normal file
@ -0,0 +1,136 @@
|
||||
# Toolchain for QNX Safety builds with the CUDA Safe toolkit (QNX SAFE stack).
|
||||
# Use this file instead of toolchain-aarch64-qnx.cmake for safe-toolkit installs.
|
||||
|
||||
set(CMAKE_SYSTEM_NAME QNX)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
set(QNX_HOST $ENV{QNX_HOST})
|
||||
set(QNX_TARGET $ENV{QNX_TARGET})
|
||||
|
||||
message(STATUS "QNX_HOST = ${QNX_HOST}")
|
||||
message(STATUS "QNX_TARGET = ${QNX_TARGET}")
|
||||
|
||||
find_program(QNX_QCC NAMES qcc PATHS "${QNX_HOST}/usr/bin")
|
||||
find_program(QNX_QPLUS NAMES q++ PATHS "${QNX_HOST}/usr/bin")
|
||||
|
||||
if(NOT QNX_QCC OR NOT QNX_QPLUS)
|
||||
message(FATAL_ERROR "Could not find qcc or q++ in QNX_HOST=${QNX_HOST}/usr/bin")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_COMPILER ${QNX_QCC})
|
||||
set(CMAKE_CXX_COMPILER ${QNX_QPLUS})
|
||||
|
||||
set(CMAKE_C_COMPILER_TARGET aarch64)
|
||||
set(CMAKE_CXX_COMPILER_TARGET aarch64)
|
||||
|
||||
# Toolkit root: required so CUDA compiler ID test does not expand empty CUDA_ROOT into -I/include, etc.
|
||||
if(CMAKE_CUDA_COMPILER)
|
||||
get_filename_component(_cuda_nvcc_dir "${CMAKE_CUDA_COMPILER}" DIRECTORY)
|
||||
get_filename_component(CUDA_TOOLKIT_ROOT "${_cuda_nvcc_dir}" DIRECTORY)
|
||||
elseif(DEFINED ENV{CUDA_PATH})
|
||||
set(CUDA_TOOLKIT_ROOT "$ENV{CUDA_PATH}")
|
||||
endif()
|
||||
if(CUDA_TOOLKIT_ROOT)
|
||||
set(CUDA_TOOLKIT_ROOT "${CUDA_TOOLKIT_ROOT}" CACHE PATH "CUDA Toolkit root")
|
||||
set(CUDA_ROOT "${CUDA_TOOLKIT_ROOT}" CACHE PATH "CUDA Toolkit root (nvcc / legacy CMake)" FORCE)
|
||||
endif()
|
||||
|
||||
if(NOT __qnx_gcc_ver)
|
||||
set(__qnx_gcc_ver "12.2.0" CACHE STRING "QNX qcc/q++ toolchain version for -V / --qpp-config (match your SDP)")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER} CACHE STRING "" FORCE)
|
||||
|
||||
set(_cuda_safe_target "aarch64-qnx")
|
||||
|
||||
# CUDA Safe toolkit flags: -safety-compat keeps the kernel-launch ABI consistent; the safe toolkit
|
||||
# ships only a shared cudart (no static/cudadevrt); -I/-L point at its headers/libs;
|
||||
# --unresolved-symbols defers libcudart's rootfs-resident deps (nvdvms_*, NvOs*) to runtime.
|
||||
if(CUDA_TOOLKIT_ROOT)
|
||||
set(_cuda_safe_inc "${CUDA_TOOLKIT_ROOT}/targets/${_cuda_safe_target}/include")
|
||||
set(_cuda_safe_lib "${CUDA_TOOLKIT_ROOT}/targets/${_cuda_safe_target}/lib")
|
||||
set(_cuda_safe_stubs "${CUDA_TOOLKIT_ROOT}/targets/${_cuda_safe_target}/lib/stubs")
|
||||
endif()
|
||||
|
||||
set(_cuda_safe_flags "-safety-compat --cudart=shared --cudadevrt=none -target-dir ${_cuda_safe_target} --qpp-config=${__qnx_gcc_ver},gcc_ntoaarch64le")
|
||||
if(CUDA_TOOLKIT_ROOT)
|
||||
string(APPEND _cuda_safe_flags " -I${_cuda_safe_inc} -L${_cuda_safe_lib} -L${_cuda_safe_stubs}")
|
||||
endif()
|
||||
string(APPEND _cuda_safe_flags " -Xlinker --unresolved-symbols=ignore-in-shared-libs")
|
||||
|
||||
set(CMAKE_C_FLAGS " \"-V${__qnx_gcc_ver},gcc_ntoaarch64le\"")
|
||||
set(CMAKE_CXX_FLAGS " \"-V${__qnx_gcc_ver},gcc_ntoaarch64le\"")
|
||||
# Host .cpp TUs are built by q++ directly, so give them the CUDA include too.
|
||||
if(CUDA_TOOLKIT_ROOT)
|
||||
string(APPEND CMAKE_C_FLAGS " -I${_cuda_safe_inc}")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -I${_cuda_safe_inc}")
|
||||
endif()
|
||||
set(CMAKE_CUDA_FLAGS " ${_cuda_safe_flags}")
|
||||
separate_arguments(_automagic_nvcc_flags UNIX_COMMAND "${_cuda_safe_flags}")
|
||||
set(AUTOMAGIC_NVCC_FLAGS ${_automagic_nvcc_flags}
|
||||
CACHE STRING "automagic feature detection flags for QNX Safe cross build")
|
||||
|
||||
# Safe toolkit has no cudadevrt/static cudart, so disable CMake's implicit runtime libs and link the
|
||||
# shared cudart ourselves. Also link the driver (-lcuda): the shared libcudart.so references safety-
|
||||
# stack symbols (nvdvms_*, NvOs*) that are transitive deps of libcuda.so.1, so the exe must pull in
|
||||
# libcuda for them to resolve/load on the target. STANDARD_LIBRARIES_INIT covers ABI checks; the
|
||||
# HOST_LINK options cover real targets.
|
||||
set(CMAKE_CUDA_RUNTIME_LIBRARY "None" CACHE STRING "CUDA runtime library (safe toolkit: linked manually)")
|
||||
set(CMAKE_CUDA_STANDARD_LIBRARIES_INIT "-lcudart -lcuda")
|
||||
if(CUDA_TOOLKIT_ROOT)
|
||||
add_link_options("$<HOST_LINK:-L${_cuda_safe_lib}>" "$<HOST_LINK:-L${_cuda_safe_stubs}>"
|
||||
"$<HOST_LINK:-lcudart>" "$<HOST_LINK:-lcuda>")
|
||||
endif()
|
||||
|
||||
# -V on the host link only; nvcc would comma-split it at the device link (which uses --qpp-config).
|
||||
add_link_options("$<HOST_LINK:-V${__qnx_gcc_ver}$<COMMA>gcc_ntoaarch64le>")
|
||||
# Defer libcudart's rootfs-resident deps to runtime.
|
||||
add_link_options("-Wl,--unresolved-symbols=ignore-in-shared-libs")
|
||||
|
||||
set(CROSS_COMPILE_FOR_QNX ON CACHE BOOL "Cross compiling for QNX platforms")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -D_QNX_SOURCE")
|
||||
string(APPEND CMAKE_CUDA_FLAGS " -D_QNX_SOURCE")
|
||||
|
||||
# cudaNvSci needs NvSci headers/libs from the QNX safety rootfs; point TARGET_FS at it. This
|
||||
# pre-seeds FindNVSCI's cache vars and auto-detects the header/lib dirs across rootfs layouts.
|
||||
if(DEFINED TARGET_FS)
|
||||
# Expand a leading ~ so paths reach the linker absolute.
|
||||
if(TARGET_FS MATCHES "^~")
|
||||
string(REGEX REPLACE "^~" "$ENV{HOME}" TARGET_FS "${TARGET_FS}")
|
||||
endif()
|
||||
get_filename_component(_nvsci_sdk_root "${TARGET_FS}" DIRECTORY)
|
||||
|
||||
# Header dir varies by rootfs layout.
|
||||
foreach(_inc "${TARGET_FS}/include" "${_nvsci_sdk_root}/include" "${TARGET_FS}/usr/include")
|
||||
if(NOT _nvsci_inc AND EXISTS "${_inc}/nvscibuf.h")
|
||||
set(_nvsci_inc "${_inc}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Lib dir varies by rootfs layout.
|
||||
foreach(_lib "${TARGET_FS}/lib-target" "${TARGET_FS}/usr/libnvidia" "${TARGET_FS}/usr/lib")
|
||||
if(NOT _nvsci_lib AND EXISTS "${_lib}/libnvscibuf.so")
|
||||
set(_nvsci_lib "${_lib}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _nvsci_inc OR NOT _nvsci_lib)
|
||||
message(FATAL_ERROR
|
||||
"Could not locate NvSci headers/libs from TARGET_FS='${TARGET_FS}'. "
|
||||
"Checked include roots for nvscibuf.h and lib roots for libnvscibuf.so. "
|
||||
"Please set TARGET_FS (or NVSCIBUF/NVSCISYNC cache vars) to a valid QNX safety rootfs.")
|
||||
endif()
|
||||
|
||||
set(NVSCIBUF_INCLUDE_DIR "${_nvsci_inc}" CACHE PATH "NvSciBuf headers (QNX safe)")
|
||||
set(NVSCISYNC_INCLUDE_DIR "${_nvsci_inc}" CACHE PATH "NvSciSync headers (QNX safe)")
|
||||
set(NVSCIBUF_LIBRARY "${_nvsci_lib}/libnvscibuf.so" CACHE FILEPATH "NvSciBuf library (QNX safe)")
|
||||
set(NVSCISYNC_LIBRARY "${_nvsci_lib}/libnvscisync.so" CACHE FILEPATH "NvSciSync library (QNX safe)")
|
||||
|
||||
# rpath-link the rootfs lib dirs so NvSci's transitive deps resolve at link time.
|
||||
foreach(_dir "${_nvsci_lib}" "${TARGET_FS}/lib-target" "${TARGET_FS}/usr/libnvidia" "${TARGET_FS}/usr/lib")
|
||||
if(IS_DIRECTORY "${_dir}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,${_dir}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath-link,${_dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
@ -35,3 +35,50 @@ add_link_options("-V${__qnx_gcc_ver},gcc_ntoaarch64le")
|
||||
set(CROSS_COMPILE_FOR_QNX ON CACHE BOOL "Cross compiling for QNX platforms")
|
||||
string(APPEND CMAKE_CXX_FLAGS " -D_QNX_SOURCE")
|
||||
string(APPEND CMAKE_CUDA_FLAGS " -D_QNX_SOURCE")
|
||||
|
||||
# cudaNvSci needs NvSci headers/libs from the QNX rootfs; point TARGET_FS at it. This pre-seeds
|
||||
# FindNVSCI's cache vars and auto-detects the header/lib dirs across rootfs layouts.
|
||||
if(DEFINED TARGET_FS)
|
||||
# Expand a leading ~ so paths reach the linker absolute.
|
||||
if(TARGET_FS MATCHES "^~")
|
||||
string(REGEX REPLACE "^~" "$ENV{HOME}" TARGET_FS "${TARGET_FS}")
|
||||
endif()
|
||||
get_filename_component(_nvsci_sdk_root "${TARGET_FS}" DIRECTORY)
|
||||
|
||||
# Header dir varies by rootfs layout. Both headers are required, so accept a dir only if it
|
||||
# holds both.
|
||||
foreach(_inc "${TARGET_FS}/include" "${_nvsci_sdk_root}/include" "${TARGET_FS}/usr/include")
|
||||
if(NOT _nvsci_inc AND EXISTS "${_inc}/nvscibuf.h" AND EXISTS "${_inc}/nvscisync.h")
|
||||
set(_nvsci_inc "${_inc}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Lib dir varies by rootfs layout. FindNVSCI needs both libraries, so accept a dir only if it
|
||||
# holds both.
|
||||
foreach(_lib "${TARGET_FS}/lib-target" "${TARGET_FS}/usr/libnvidia" "${TARGET_FS}/usr/lib")
|
||||
if(NOT _nvsci_lib AND EXISTS "${_lib}/libnvscibuf.so" AND EXISTS "${_lib}/libnvscisync.so")
|
||||
set(_nvsci_lib "${_lib}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# A rootfs without NvSci is not an error: FindNVSCI then reports NvSci as missing and only the
|
||||
# NvSci samples are skipped.
|
||||
if(_nvsci_inc AND _nvsci_lib)
|
||||
set(NVSCIBUF_INCLUDE_DIR "${_nvsci_inc}" CACHE PATH "NvSciBuf headers")
|
||||
set(NVSCISYNC_INCLUDE_DIR "${_nvsci_inc}" CACHE PATH "NvSciSync headers")
|
||||
set(NVSCIBUF_LIBRARY "${_nvsci_lib}/libnvscibuf.so" CACHE FILEPATH "NvSciBuf library")
|
||||
set(NVSCISYNC_LIBRARY "${_nvsci_lib}/libnvscisync.so" CACHE FILEPATH "NvSciSync library")
|
||||
|
||||
# rpath-link the rootfs lib dirs so NvSci's transitive deps resolve at link time.
|
||||
# NvSci pulls in NvRm/NvOs/NvSciIpc, which can sit in a lib dir of their own.
|
||||
foreach(_dir "${_nvsci_lib}" "${TARGET_FS}/lib-target" "${_nvsci_sdk_root}/lib-target"
|
||||
"${TARGET_FS}/usr/libnvidia" "${TARGET_FS}/usr/lib")
|
||||
if(IS_DIRECTORY "${_dir}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath-link,${_dir}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath-link,${_dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "NvSci headers/libs not found in TARGET_FS='${TARGET_FS}'")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -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_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_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_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user