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:
Dheemanth
2026-05-13 15:13:18 -07:00
committed by GitHub
parent a4526d5229
commit aeab82ff30
2072 changed files with 15077 additions and 1970 deletions

View 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
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View 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()

View 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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}

View 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_