mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-13 00:08:26 +08:00
CUDA 13.2 samples update (#432)
- Added Python samples for CUDA Python 1.0 release - Renamed top-level `Samples` directory to `cpp` to accommodate Python samples.
This commit is contained in:
18
cpp/5_Domain_Specific/dwtHaar1D/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/5_Domain_Specific/dwtHaar1D/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
7
cpp/5_Domain_Specific/dwtHaar1D/.vscode/extensions.json
vendored
Normal file
7
cpp/5_Domain_Specific/dwtHaar1D/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
40
cpp/5_Domain_Specific/dwtHaar1D/CMakeLists.txt
Normal file
40
cpp/5_Domain_Specific/dwtHaar1D/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(dwtHaar1D 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 target for dwtHaar1D
|
||||
add_executable(dwtHaar1D dwtHaar1D.cu)
|
||||
|
||||
target_compile_options(dwtHaar1D PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(dwtHaar1D PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(dwtHaar1D PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
add_custom_command(TARGET dwtHaar1D POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
32
cpp/5_Domain_Specific/dwtHaar1D/README.md
Normal file
32
cpp/5_Domain_Specific/dwtHaar1D/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# dwtHaar1D - 1D Discrete Haar Wavelet Decomposition
|
||||
|
||||
## Description
|
||||
|
||||
Discrete Haar wavelet decomposition for 1D signals with a length which is a power of 2.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Image Processing, Video Compression
|
||||
|
||||
## 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, cudaMemcpy, cudaFree
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## References (for more details)
|
||||
4096
cpp/5_Domain_Specific/dwtHaar1D/data/regression.gold.dat
Normal file
4096
cpp/5_Domain_Specific/dwtHaar1D/data/regression.gold.dat
Normal file
File diff suppressed because it is too large
Load Diff
16384
cpp/5_Domain_Specific/dwtHaar1D/data/regression_2_14.gold.dat
Normal file
16384
cpp/5_Domain_Specific/dwtHaar1D/data/regression_2_14.gold.dat
Normal file
File diff suppressed because it is too large
Load Diff
262144
cpp/5_Domain_Specific/dwtHaar1D/data/regression_2_18.gold.dat
Normal file
262144
cpp/5_Domain_Specific/dwtHaar1D/data/regression_2_18.gold.dat
Normal file
File diff suppressed because it is too large
Load Diff
4096
cpp/5_Domain_Specific/dwtHaar1D/data/signal.dat
Normal file
4096
cpp/5_Domain_Specific/dwtHaar1D/data/signal.dat
Normal file
File diff suppressed because it is too large
Load Diff
16384
cpp/5_Domain_Specific/dwtHaar1D/data/signal_2_14.dat
Normal file
16384
cpp/5_Domain_Specific/dwtHaar1D/data/signal_2_14.dat
Normal file
File diff suppressed because it is too large
Load Diff
262144
cpp/5_Domain_Specific/dwtHaar1D/data/signal_2_18.dat
Normal file
262144
cpp/5_Domain_Specific/dwtHaar1D/data/signal_2_18.dat
Normal file
File diff suppressed because it is too large
Load Diff
386
cpp/5_Domain_Specific/dwtHaar1D/dwtHaar1D.cu
Normal file
386
cpp/5_Domain_Specific/dwtHaar1D/dwtHaar1D.cu
Normal file
@@ -0,0 +1,386 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 1D DWT for Haar wavelet and signals with a length which is a power of 2.
|
||||
* The code reduces bank conflicts and non-coalesced reads / writes as
|
||||
* appropriate but does not fully remove them because the computational
|
||||
* overhead to achieve this would outweighs the benefit (see inline comments
|
||||
* for more details).
|
||||
* Large signals are subdivided into sub-signals with 512 elements and the
|
||||
* wavelet transform for these is computed with one block over 10 decomposition
|
||||
* levels. The resulting signal consisting of the approximation coefficients at
|
||||
* level X is then processed in a subsequent step on the device. This requires
|
||||
* interblock synchronization which is only possible on host side.
|
||||
* Detail coefficients which have been computed are not further referenced
|
||||
* during the decomposition so that they can be stored directly in their final
|
||||
* position in global memory. The transform and its storing scheme preserve
|
||||
* locality in the coefficients so that these writes are coalesced.
|
||||
* Approximation coefficients are stored in shared memory because they are
|
||||
* needed to compute the subsequent decomposition step. The top most
|
||||
* approximation coefficient for a sub-signal processed by one block is stored
|
||||
* in a special global memory location to simplify the processing after the
|
||||
* interblock synchronization.
|
||||
* Most books on wavelets explain the Haar wavelet decomposition. A good freely
|
||||
* available resource is the Wavelet primer by Stollnitz et al.
|
||||
* http://grail.cs.washington.edu/projects/wavelets/article/wavelet1.pdf
|
||||
* http://grail.cs.washington.edu/projects/wavelets/article/wavelet2.pdf
|
||||
* The basic of all Wavelet transforms is to decompose a signal into
|
||||
* approximation (a) and detail (d) coefficients where the detail tends to be
|
||||
* small or zero which allows / simplifies compression. The following "graphs"
|
||||
* demonstrate the transform for a signal
|
||||
* of length eight. The index always describes the decomposition level where
|
||||
* a coefficient arises. The input signal is interpreted as approximation signal
|
||||
* at level 0. The coefficients computed on the device are stored in the same
|
||||
* scheme as in the example. This data structure is particularly well suited for
|
||||
* compression and also preserves the hierarchical structure of the
|
||||
decomposition.
|
||||
|
||||
-------------------------------------------------
|
||||
| a_0 | a_0 | a_0 | a_0 | a_0 | a_0 | a_0 | a_0 |
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
| a_1 | a_1 | a_1 | a_1 | d_1 | d_1 | d_1 | d_1 |
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
| a_2 | a_2 | d_2 | d_2 | d_1 | d_1 | d_1 | d_1 |
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
| a_3 | d_3 | d_2 | d_2 | d_1 | d_1 | d_1 | d_1 |
|
||||
-------------------------------------------------
|
||||
|
||||
* Host code.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
// includes, system
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// includes, project
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_functions.h>
|
||||
|
||||
// constants which are used in host and device code
|
||||
#define INV_SQRT_2 0.70710678118654752440f;
|
||||
const unsigned int LOG_NUM_BANKS = 4;
|
||||
const unsigned int NUM_BANKS = 16;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// includes, kernels
|
||||
#include "dwtHaar1D_kernel.cuh"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// declaration, forward
|
||||
void runTest(int argc, char **argv);
|
||||
bool getLevels(unsigned int len, unsigned int *levels);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Program main
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// run test
|
||||
runTest(argc, argv);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Perform the wavelet decomposition
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void runTest(int argc, char **argv)
|
||||
{
|
||||
bool bResult = false; // flag for final validation of the results
|
||||
|
||||
char *s_fname = NULL, *r_gold_fname = NULL;
|
||||
char r_fname[256];
|
||||
const char usage[] = {"\nUsage:\n"
|
||||
" dwtHaar1D --signal=<signal_file> --result=<result_file> "
|
||||
"--gold=<gold_file>\n\n"
|
||||
" <signal_file> Input file containing the signal\n"
|
||||
" <result_file> Output file storing the result of the wavelet "
|
||||
"decomposition\n"
|
||||
" <gold_file> Input file containing the reference result of the "
|
||||
"wavelet decomposition\n"
|
||||
"\nExample:\n"
|
||||
" ./dwtHaar1D\n"
|
||||
" --signal=signal.dat\n"
|
||||
" --result=result.dat\n"
|
||||
" --gold=regression.gold.dat\n"};
|
||||
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
// use command-line specified CUDA device, otherwise use device with highest
|
||||
// Gflops/s
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
// file names, either specified as cmd line args or use default
|
||||
if (argc == 4) {
|
||||
char *tmp_sfname, *tmp_rfname, *tmp_goldfname;
|
||||
|
||||
if ((getCmdLineArgumentString(argc, (const char **)argv, "signal", &tmp_sfname) != true)
|
||||
|| (getCmdLineArgumentString(argc, (const char **)argv, "result", &tmp_rfname) != true)
|
||||
|| (getCmdLineArgumentString(argc, (const char **)argv, "gold", &tmp_goldfname) != true)) {
|
||||
fprintf(stderr, "Invalid input syntax.\n%s", usage);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
s_fname = sdkFindFilePath(tmp_sfname, argv[0]);
|
||||
r_gold_fname = sdkFindFilePath(tmp_goldfname, argv[0]);
|
||||
strcpy(r_fname, tmp_rfname);
|
||||
}
|
||||
else {
|
||||
s_fname = sdkFindFilePath("signal.dat", argv[0]);
|
||||
r_gold_fname = sdkFindFilePath("regression.gold.dat", argv[0]);
|
||||
strcpy(r_fname, "result.dat");
|
||||
}
|
||||
|
||||
printf("source file = \"%s\"\n", s_fname);
|
||||
printf("reference file = \"%s\"\n", r_fname);
|
||||
printf("gold file = \"%s\"\n", r_gold_fname);
|
||||
|
||||
// read in signal
|
||||
unsigned int slength = 0;
|
||||
float *signal = NULL;
|
||||
|
||||
if (s_fname == NULL) {
|
||||
fprintf(stderr, "Cannot find the file containing the signal.\n%s", usage);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sdkReadFile(s_fname, &signal, &slength, false) == true) {
|
||||
printf("Reading signal from \"%s\"\n", s_fname);
|
||||
}
|
||||
else {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// get the number of decompositions necessary to perform a full decomposition
|
||||
unsigned int dlevels_complete = 0;
|
||||
|
||||
if (true != getLevels(slength, &dlevels_complete)) {
|
||||
// error message
|
||||
fprintf(stderr, "Signal length not supported.\n");
|
||||
// cleanup and abort
|
||||
free(signal);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// device in data
|
||||
float *d_idata = NULL;
|
||||
// device out data
|
||||
float *d_odata = NULL;
|
||||
// device approx_final data
|
||||
float *approx_final = NULL;
|
||||
// The very final approximation coefficient has to be written to the output
|
||||
// data, all others are reused as input data in the next global step and
|
||||
// therefore have to be written to the input data again.
|
||||
// The following flag indicates where to copy approx_final data
|
||||
// - 0 is input, 1 is output
|
||||
int approx_is_input;
|
||||
|
||||
// allocate device mem
|
||||
const unsigned int smem_size = sizeof(float) * slength;
|
||||
checkCudaErrors(cudaMalloc((void **)&d_idata, smem_size));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_odata, smem_size));
|
||||
checkCudaErrors(cudaMalloc((void **)&approx_final, smem_size));
|
||||
// copy input data to device
|
||||
checkCudaErrors(cudaMemcpy(d_idata, signal, smem_size, cudaMemcpyHostToDevice));
|
||||
|
||||
// total number of threads
|
||||
// in the first decomposition step always one thread computes the average and
|
||||
// detail signal for one pair of adjacent values
|
||||
unsigned int num_threads_total_left = slength / 2;
|
||||
// decomposition levels performed in the current / next step
|
||||
unsigned int dlevels_step = dlevels_complete;
|
||||
|
||||
// 1D signal so the arrangement of elements is also 1D
|
||||
dim3 block_size;
|
||||
dim3 grid_size;
|
||||
|
||||
// number of decomposition levels left after one iteration on the device
|
||||
unsigned int dlevels_left = dlevels_complete;
|
||||
|
||||
// if less or equal 1k elements, then the data can be processed in one block,
|
||||
// this avoids the Wait-For-Idle (WFI) on host side which is necessary if the
|
||||
// computation is split across multiple SM's if enough input data
|
||||
if (dlevels_complete <= 10) {
|
||||
// decomposition can be performed at once
|
||||
block_size.x = num_threads_total_left;
|
||||
approx_is_input = 0;
|
||||
}
|
||||
else {
|
||||
// 512 threads per block
|
||||
grid_size.x = (num_threads_total_left / 512);
|
||||
block_size.x = 512;
|
||||
|
||||
// 512 threads corresponds to 10 decomposition steps
|
||||
dlevels_step = 10;
|
||||
dlevels_left -= 10;
|
||||
|
||||
approx_is_input = 1;
|
||||
}
|
||||
|
||||
// Initialize d_odata to 0.0f
|
||||
initValue<<<grid_size, block_size>>>(d_odata, 0.0f);
|
||||
|
||||
// do until full decomposition is accomplished
|
||||
while (0 != num_threads_total_left) {
|
||||
// double the number of threads as bytes
|
||||
unsigned int mem_shared = (2 * block_size.x) * sizeof(float);
|
||||
// extra memory requirements to avoid bank conflicts
|
||||
mem_shared += ((2 * block_size.x) / NUM_BANKS) * sizeof(float);
|
||||
|
||||
// run kernel
|
||||
dwtHaar1D<<<grid_size, block_size, mem_shared>>>(
|
||||
d_idata, d_odata, approx_final, dlevels_step, num_threads_total_left, block_size.x);
|
||||
|
||||
// Copy approx_final to appropriate location
|
||||
if (approx_is_input) {
|
||||
checkCudaErrors(cudaMemcpy(d_idata, approx_final, grid_size.x * 4, cudaMemcpyDeviceToDevice));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cudaMemcpy(d_odata, approx_final, grid_size.x * 4, cudaMemcpyDeviceToDevice));
|
||||
}
|
||||
|
||||
// update level variables
|
||||
if (dlevels_left < 10) {
|
||||
// approx_final = d_odata;
|
||||
approx_is_input = 0;
|
||||
}
|
||||
|
||||
// more global steps necessary
|
||||
dlevels_step = (dlevels_left > 10) ? dlevels_left - 10 : dlevels_left;
|
||||
dlevels_left -= 10;
|
||||
|
||||
// after each step only half the threads are used any longer
|
||||
// therefore after 10 steps 2^10 less threads
|
||||
num_threads_total_left = num_threads_total_left >> 10;
|
||||
|
||||
// update block and grid size
|
||||
grid_size.x = (num_threads_total_left / 512) + (0 != (num_threads_total_left % 512)) ? 1 : 0;
|
||||
|
||||
if (grid_size.x <= 1) {
|
||||
block_size.x = num_threads_total_left;
|
||||
}
|
||||
}
|
||||
|
||||
// get the result back from the server
|
||||
// allocate mem for the result
|
||||
float *odata = (float *)malloc(smem_size);
|
||||
checkCudaErrors(cudaMemcpy(odata, d_odata, smem_size, cudaMemcpyDeviceToHost));
|
||||
|
||||
// post processing
|
||||
// write file for regression test
|
||||
if (r_fname == NULL) {
|
||||
fprintf(stderr,
|
||||
"Cannot write the output file storing the result of the wavelet "
|
||||
"decomposition.\n%s",
|
||||
usage);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sdkWriteFile(r_fname, odata, slength, 0.001f, false) == true) {
|
||||
printf("Writing result to \"%s\"\n", r_fname);
|
||||
}
|
||||
else {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// load the reference solution
|
||||
unsigned int len_reference = 0;
|
||||
float *reference = NULL;
|
||||
|
||||
if (r_gold_fname == NULL) {
|
||||
fprintf(stderr,
|
||||
"Cannot read the file containing the reference result of the "
|
||||
"wavelet decomposition.\n%s",
|
||||
usage);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sdkReadFile(r_gold_fname, &reference, &len_reference, false) == true) {
|
||||
printf("Reading reference result from \"%s\"\n", r_gold_fname);
|
||||
}
|
||||
else {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
assert(slength == len_reference);
|
||||
|
||||
// compare the computed solution and the reference
|
||||
bResult = (bool)sdkCompareL2fe(reference, odata, slength, 0.001f);
|
||||
free(reference);
|
||||
|
||||
// free allocated host and device memory
|
||||
checkCudaErrors(cudaFree(d_odata));
|
||||
checkCudaErrors(cudaFree(d_idata));
|
||||
checkCudaErrors(cudaFree(approx_final));
|
||||
|
||||
free(signal);
|
||||
free(odata);
|
||||
free(s_fname);
|
||||
free(r_gold_fname);
|
||||
|
||||
printf(bResult ? "Test success!\n" : "Test failure!\n");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Get number of decomposition levels to perform a full decomposition
|
||||
//! Also check if the input signal size is suitable
|
||||
//! @return true if the number of decomposition levels could be determined
|
||||
//! and the signal length is supported by the implementation,
|
||||
//! otherwise false
|
||||
//! @param len length of input signal
|
||||
//! @param levels number of decomposition levels necessary to perform a full
|
||||
//! decomposition
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool getLevels(unsigned int len, unsigned int *levels)
|
||||
{
|
||||
bool retval = false;
|
||||
|
||||
// currently signals up to a length of 2^20 supported
|
||||
for (unsigned int i = 0; i < 20; ++i) {
|
||||
if (len == (1 << i)) {
|
||||
*levels = i;
|
||||
retval = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
248
cpp/5_Domain_Specific/dwtHaar1D/dwtHaar1D_kernel.cuh
Normal file
248
cpp/5_Domain_Specific/dwtHaar1D/dwtHaar1D_kernel.cuh
Normal file
@@ -0,0 +1,248 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 1D DWT for Haar wavelet and signals with a length which is a power of 2.
|
||||
* The code reduces bank conflicts and non-coalesced reads / writes as
|
||||
* appropriate but does not fully remove them because the computational
|
||||
* overhead to achieve this would outweighs the benefit (see inline comments
|
||||
* for more details).
|
||||
* Large signals are subdivided into sub-signals with 512 elements and the
|
||||
* wavelet transform for these is computed with one block over 10 decomposition
|
||||
* levels. The resulting signal consisting of the approximation coefficients at
|
||||
* level X is then processed in a subsequent step on the device. This requires
|
||||
* interblock synchronization which is only possible on host side.
|
||||
* Detail coefficients which have been computed are not further referenced
|
||||
* during the decomposition so that they can be stored directly in their final
|
||||
* position in global memory. The transform and its storing scheme preserve
|
||||
* locality in the coefficients so that these writes are coalesced.
|
||||
* Approximation coefficients are stored in shared memory because they are
|
||||
* needed to compute the subsequent decomposition step. The top most
|
||||
* approximation coefficient for a sub-signal processed by one block is stored
|
||||
* in a special global memory location to simplify the processing after the
|
||||
* interblock synchronization.
|
||||
* Most books on wavelets explain the Haar wavelet decomposition. A good freely
|
||||
* available resource is the Wavelet primer by Stollnitz et al.
|
||||
* http://grail.cs.washington.edu/projects/wavelets/article/wavelet1.pdf
|
||||
* http://grail.cs.washington.edu/projects/wavelets/article/wavelet2.pdf
|
||||
* The basic of all Wavelet transforms is to decompose a signal into
|
||||
* approximation (a) and detail (d) coefficients where the detail tends to be
|
||||
* small or zero which allows / simplifies compression. The following "graphs"
|
||||
* demonstrate the transform for a signal
|
||||
* of length eight. The index always describes the decomposition level where
|
||||
* a coefficient arises. The input signal is interpreted as approximation signal
|
||||
* at level 0. The coefficients computed on the device are stored in the same
|
||||
* scheme as in the example. This data structure is particularly well suited for
|
||||
* compression and also preserves the hierarchical structure of the
|
||||
decomposition.
|
||||
|
||||
-------------------------------------------------
|
||||
| a_0 | a_0 | a_0 | a_0 | a_0 | a_0 | a_0 | a_0 |
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
| a_1 | a_1 | a_1 | a_1 | d_1 | d_1 | d_1 | d_1 |
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
| a_2 | a_2 | d_2 | d_2 | d_1 | d_1 | d_1 | d_1 |
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
| a_3 | d_3 | d_2 | d_2 | d_1 | d_1 | d_1 | d_1 |
|
||||
-------------------------------------------------
|
||||
|
||||
* Device Code.
|
||||
*/
|
||||
|
||||
#ifndef _DWTHAAR1D_KERNEL_H_
|
||||
#define _DWTHAAR1D_KERNEL_H_
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! @param id input data
|
||||
//! @param od output data
|
||||
//! @param value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__global__ void initValue(float *od, float value)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
// position of write into global memory
|
||||
unsigned int index = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
|
||||
od[index] = value;
|
||||
|
||||
// sync after each decomposition step
|
||||
cg::sync(cta);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//! Compute partial wavelet decomposition on the GPU using Haar basis
|
||||
//! For each thread block the full decomposition is computed but these results
|
||||
//! have to be combined
|
||||
//! Use one thread to perform the full decomposition
|
||||
//! @param id input data
|
||||
//! @param od output data
|
||||
//! @param approx_final place to store the final approximation coefficient for
|
||||
//! the subsignal
|
||||
//! @param dlevels number of decomposition levels for this transform
|
||||
//! @param slength_step_half half signal length for current decomposition
|
||||
//! level (offset for storing detail coefficients in
|
||||
//! global memory
|
||||
//! @param bdim block dimension
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__global__ void dwtHaar1D(float *id,
|
||||
float *od,
|
||||
float *approx_final,
|
||||
const unsigned int dlevels,
|
||||
const unsigned int slength_step_half,
|
||||
const int bdim)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
|
||||
// shared memory for part of the signal
|
||||
extern __shared__ float shared[];
|
||||
|
||||
// thread runtime environment, 1D parametrization
|
||||
const int gdim = gridDim.x;
|
||||
// const int bdim = blockDim.x;
|
||||
const int bid = blockIdx.x;
|
||||
const int tid = threadIdx.x;
|
||||
|
||||
// global thread id (w.r.t. to total data set)
|
||||
const int tid_global = (bid * bdim) + tid;
|
||||
unsigned int idata = (bid * (2 * bdim)) + tid;
|
||||
|
||||
// read data from global memory
|
||||
shared[tid] = id[idata];
|
||||
shared[tid + bdim] = id[idata + bdim];
|
||||
cg::sync(cta);
|
||||
|
||||
// this operation has a two way bank conflicts for all threads, this are two
|
||||
// additional cycles for each warp -- all alternatives to avoid this bank
|
||||
// conflict are more expensive than the one cycle introduced by serialization
|
||||
float data0 = shared[2 * tid];
|
||||
float data1 = shared[(2 * tid) + 1];
|
||||
cg::sync(cta);
|
||||
|
||||
// detail coefficient, not further referenced so directly store in
|
||||
// global memory
|
||||
od[tid_global + slength_step_half] = (data0 - data1) * INV_SQRT_2;
|
||||
|
||||
// offset to avoid bank conflicts
|
||||
// see the scan example for a more detailed description
|
||||
unsigned int atid = tid + (tid >> LOG_NUM_BANKS);
|
||||
|
||||
// approximation coefficient
|
||||
// store in shared memory for further decomposition steps in this global step
|
||||
shared[atid] = (data0 + data1) * INV_SQRT_2;
|
||||
|
||||
// all threads have to write approximation coefficient to shared memory before
|
||||
// next steps can take place
|
||||
cg::sync(cta);
|
||||
|
||||
// early out if possible
|
||||
// the compiler removes this part from the source because dlevels is
|
||||
// a constant shader input
|
||||
// note: syncthreads in bodies of branches can lead to dead-locks unless
|
||||
// the condition evaluates the same way for ALL threads of a block, as in
|
||||
// this case
|
||||
if (dlevels > 1) {
|
||||
// offset to second element in shared element which has to be used for the
|
||||
// decomposition, effectively 2^(i - 1)
|
||||
unsigned int offset_neighbor = 1;
|
||||
// number of active threads per decomposition level
|
||||
// identical to the offset for the detail coefficients
|
||||
unsigned int num_threads = bdim >> 1;
|
||||
|
||||
// index for the first element of the pair to process
|
||||
// the representation is still compact (and therefore still tid * 2)
|
||||
// because the first step operated on registers and only the result has been
|
||||
// written to shared memory
|
||||
unsigned int idata0 = tid * 2;
|
||||
|
||||
// offset levels to make the loop more efficient
|
||||
for (unsigned int i = 1; i < dlevels; ++i) {
|
||||
// Non-coalesced writes occur if the number of active threads becomes
|
||||
// less than 16 for a block because the start address for the first
|
||||
// block is not always aligned with 64 byte which is necessary for
|
||||
// coalesced access. However, the problem only occurs at high levels
|
||||
// with only a small number of active threads so that the total number of
|
||||
// non-coalesced access is rather small and does not justify the
|
||||
// computations which are necessary to avoid these uncoalesced writes
|
||||
// (this has been tested and verified)
|
||||
if (tid < num_threads) {
|
||||
// update stride, with each decomposition level the stride grows by a
|
||||
// factor of 2
|
||||
unsigned int idata1 = idata0 + offset_neighbor;
|
||||
|
||||
// position of write into global memory
|
||||
unsigned int g_wpos = (num_threads * gdim) + (bid * num_threads) + tid;
|
||||
|
||||
// compute wavelet decomposition step
|
||||
|
||||
// offset to avoid bank conflicts
|
||||
unsigned int c_idata0 = idata0 + (idata0 >> LOG_NUM_BANKS);
|
||||
unsigned int c_idata1 = idata1 + (idata1 >> LOG_NUM_BANKS);
|
||||
|
||||
// detail coefficient, not further modified so directly store
|
||||
// in global memory
|
||||
od[g_wpos] = (shared[c_idata0] - shared[c_idata1]) * INV_SQRT_2;
|
||||
|
||||
// approximation coefficient
|
||||
// note that the representation in shared memory becomes rather sparse
|
||||
// (with a lot of holes inbetween) but the storing scheme in global
|
||||
// memory guarantees that the common representation (approx, detail_0,
|
||||
// detail_1, ...)
|
||||
// is achieved
|
||||
shared[c_idata0] = (shared[c_idata0] + shared[c_idata1]) * INV_SQRT_2;
|
||||
|
||||
// update storage offset for details
|
||||
num_threads = num_threads >> 1; // div 2
|
||||
offset_neighbor <<= 1; // mul 2
|
||||
idata0 = idata0 << 1; // mul 2
|
||||
}
|
||||
|
||||
// sync after each decomposition step
|
||||
cg::sync(cta);
|
||||
}
|
||||
|
||||
// write the top most level element for the next decomposition steps
|
||||
// which are performed after an interlock synchronization on host side
|
||||
if (0 == tid) {
|
||||
approx_final[bid] = shared[0];
|
||||
}
|
||||
|
||||
} // end early out if possible
|
||||
}
|
||||
|
||||
#endif // #ifndef _DWTHAAR1D_KERNEL_H_
|
||||
Reference in New Issue
Block a user