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:
Dheemanth
2026-09-09 15:07:08 -07:00
committed by GitHub
parent b7c5481c55
commit 5443602d89
871 changed files with 6843 additions and 321849 deletions

View File

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

View File

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

View File

@@ -1,34 +1,21 @@
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(simpleMultiGPU 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 simpleMultiGPU
add_executable(simpleMultiGPU simpleMultiGPU.cu)
target_compile_options(simpleMultiGPU PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
target_compile_features(simpleMultiGPU PRIVATE cxx_std_17 cuda_std_17)
set_target_properties(simpleMultiGPU PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
# Include installation configuration
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
include(InstallSamples)
setup_samples_install()

View File

@@ -1,32 +1,130 @@
# simpleMultiGPU - Simple Multi-GPU
# Sample: simpleMultiGPU
## Description
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.
A multi-GPU reduction demo: the input vector is split across every GPU in the system, each
GPU sums its slice concurrently, and the host combines the per-GPU results. Each GPU gets its
own CUDA stream, so the H2D copy → reduction kernel → D2H copy pipelines run in parallel across
devices. The device-side reduction uses `cub::BlockReduce`; the host finishes the sum across
blocks and across GPUs and checks it against a CPU reference. The whole GPU phase is timed with
CUDA events.
## What You'll Learn
- Enumerating and driving multiple GPUs with `cudaGetDeviceCount` and `cudaSetDevice`
- Partitioning a workload across GPUs and running them concurrently, one CUDA stream per GPU
- Overlapping H2D copy, kernel, and D2H copy with `cudaMemcpyAsync` on independent streams
- Reducing within a block using `cub::BlockReduce`, then finishing the reduction on the host
- Timing GPU work with CUDA events (`cudaEventRecord` / `cudaEventElapsedTime`)
- Why pinned host memory (`cudaMallocHost`) is required for `cudaMemcpyAsync` to be asynchronous
## Key Concepts
Asynchronous Data Transfers, CUDA Streams and Events, Multithreading, Multi-GPU
### One Stream per GPU
## Supported SM Architectures
The work is held in a `std::vector<TGPUplan>` with one entry per device, each carrying its own stream,
buffers, and data slice. Selecting a device with `cudaSetDevice` and issuing async work on that
device's stream lets all GPUs run at the same time:
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
```text
GPU 0 stream: [H2D slice 0] → [reduceKernel] → [D2H partials 0]
GPU 1 stream: [H2D slice 1] → [reduceKernel] → [D2H partials 1]
...
```
## Supported OSes
The host launches every GPU's pipeline before synchronizing any of them, so the copies and kernels
overlap across devices.
Linux, Windows
### Multi-Level Reduction
## Supported CPU Architecture
The reduction happens in three stages. On each GPU, `reduceKernel` launches `BLOCK_N` blocks of `THREAD_N`
threads: every thread accumulates a grid-strided partial sum, then `cub::BlockReduce` combines the
per-thread partials within its block and thread 0 writes one value per block. The host then adds the
`BLOCK_N` per-block partials into that GPU's sum, and finally adds the per-GPU sums together.
x86_64, armv7l
### Pinned Host Memory
## CUDA APIs involved
`cudaMemcpyAsync` only runs asynchronously when the host buffer is page-locked (pinned), so the OS
cannot move the pages mid-transfer; with ordinary pageable memory the copy falls back to synchronous
behavior. Both the input slice and the partial-sum buffer are allocated with `cudaMallocHost`.
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
cudaStreamDestroy, cudaFree, cudaMallocHost, cudaSetDevice, cudaFreeHost, cudaStreamSynchronize, cudaMalloc, cudaMemcpyAsync, cudaStreamCreate, cudaGetDeviceCount
### Timing with CUDA Events
## Prerequisites
CUDA events are recorded on device 0's stream to bracket the GPU phase. `cudaEventElapsedTime` returns
the milliseconds between the recorded events.
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
## Key APIs
## References (for more details)
### CUDA Runtime
- `cudaGetDeviceCount` — count the CUDA-capable GPUs in the system
- `cudaSetDevice` — select the active GPU for subsequent CUDA calls
- `cudaStreamCreate` / `cudaStreamDestroy` — per-GPU stream that orders each pipeline
- `cudaMalloc` / `cudaFree` — allocate and free device memory
- `cudaMallocHost` / `cudaFreeHost` — allocate and free pinned host memory
- `cudaMemcpyAsync` — non-blocking H2D and D2H transfers on a stream
- `cudaStreamSynchronize` — block the host until a GPU's stream is done
- `cudaEventCreate` / `cudaEventRecord` / `cudaEventSynchronize` / `cudaEventElapsedTime` / `cudaEventDestroy` — event-based timing
### CCCL / CUB
- `cub::BlockReduce` — block-wide reduction inside the kernel
## Requirements
### Hardware
- Two or more NVIDIA GPUs with Compute Capability 7.5 or higher
### Software
- CMake 3.20 or newer
- A C++17-capable host compiler
## How to Build
See the [top-level README](../../../README.md#building-cuda-samples) for full build instructions, including how to build all samples or a single sample standalone.
## How to Run
```bash
./simpleMultiGPU
```
No command-line arguments are required. The sample uses every GPU it detects, and exits without
running if it finds fewer than two.
## Expected Output
```text
Starting simpleMultiGPU
CUDA-capable device count: 2
Generating input data...
Computing with 2 GPUs...
GPU Processing time: 8.138752 (ms)
Computing with Host CPU...
Comparing GPU and Host CPU results...
GPU sum: 16777294.000000
CPU sum: 16777294.395033
Relative difference: 2.354566E-08
```
**Reading the output:**
- The device count and processing time depend on your system
- The sums are the same every run because the input is filled from an unseeded `rand()`
- The sample passes when the GPU and CPU results agree to within a relative difference of `1e-5`
## Files
- `simpleMultiGPU.cu` — reduction kernel and the multi-GPU driver
- `README.md` — this file
- `CMakeLists.txt` — build configuration
## See Also
- [CUDA Programming Guide — CUDA Streams](https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/asynchronous-execution.html#cuda-streams)
- [CUDA Programming Guide — CUDA Events](https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/asynchronous-execution.html#cuda-events)
- [CUDA Core Compute Libraries — What is CUB?](https://nvidia.github.io/cccl/unstable/cub/index.html#what-is-cub)

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,46 +26,54 @@
*/
/*
* This application demonstrates how to use the CUDA API to use multiple GPUs,
* with an emphasis on simple illustration of the techniques (not on
* performance).
* Sums a large vector across every GPU in the system.
*
* Note that in order to detect multiple GPUs in your system you have to disable
* SLI in the nvidia control panel. Otherwise only one GPU is visible to the
* application. On the other side, you can still extend your desktop to screens
* attached to both GPUs.
* The input is split into one slice per GPU, each with its own stream, so the H2D copy,
* reduction kernel, and D2H copy run concurrently across devices. cub::BlockReduce reduces
* within each thread block; the host then adds the per-block values into a per-GPU total and
* combines those into the final sum, checked against a CPU reference. CUDA events time the
* GPU phase.
*/
// System includes
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
// CUDA runtime
#include <cuda_runtime.h>
// helper functions and utilities to work with CUDA
#include <helper_cuda.h>
#include <helper_functions.h>
// CCCL / CUB block-wide reduction
#include <cub/cub.cuh>
#ifndef MAX
#define MAX(a, b) (a > b ? a : b)
#endif
#include "simpleMultiGPU.h"
////////////////////////////////////////////////////////////////////////////////
// Data configuration
////////////////////////////////////////////////////////////////////////////////
const int MAX_GPU_COUNT = 32;
const int DATA_N = 1048576 * 32;
////////////////////////////////////////////////////////////////////////////////
// Simple reduction kernel.
// Refer to the 'reduction' CUDA Sample describing
// reduction optimization strategies
////////////////////////////////////////////////////////////////////////////////
__global__ static void reduceKernel(float *d_Result, float *d_Input, int N)
// Everything one GPU needs: its slice of the input, its buffers, and its stream
struct TGPUplan
{
int dataN; // elements assigned to this GPU
float *h_Data; // pinned input slice
float *d_Data; // the slice, on the device
float *d_Sum; // one partial sum per block
float *h_Sum_from_device; // those partial sums, copied back
cudaStream_t stream; // orders this GPU's copies and kernel
};
// Data configuration
constexpr int DATA_N = 1048576 * 32;
// Reduction launch configuration
constexpr int BLOCK_N = 32; // thread blocks launched per GPU
constexpr int THREAD_N = 256; // threads per block
// Per-block reduction kernel.
// Threads walk the input in a grid-stride loop, then cub::BlockReduce combines their totals.
// Thread 0 writes one partial sum per block (BLOCK_N values total); the host
// adds those up to obtain this GPU's final sum.
__global__ static void reduceKernel(float *d_Result, const float *d_Input, int N)
{
using BlockReduce = cub::BlockReduce<float, THREAD_N>;
__shared__ typename BlockReduce::TempStorage temp_storage;
const int tid = blockIdx.x * blockDim.x + threadIdx.x;
const int threadN = gridDim.x * blockDim.x;
float sum = 0;
@@ -73,160 +81,145 @@ __global__ static void reduceKernel(float *d_Result, float *d_Input, int N)
for (int pos = tid; pos < N; pos += threadN)
sum += d_Input[pos];
d_Result[tid] = sum;
// Reduce the per-thread partial sums across the block (result valid in thread 0)
sum = BlockReduce(temp_storage).Sum(sum);
if (threadIdx.x == 0)
d_Result[blockIdx.x] = sum;
}
////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
int main()
{
// Solver config
TGPUplan plan[MAX_GPU_COUNT];
// GPU reduction results
float h_SumGPU[MAX_GPU_COUNT];
float sumGPU;
double sumCPU, diff;
int i, j, gpuBase, GPU_N;
const int BLOCK_N = 32;
const int THREAD_N = 256;
const int ACCUM_N = BLOCK_N * THREAD_N;
printf("Starting simpleMultiGPU\n");
checkCudaErrors(cudaGetDeviceCount(&GPU_N));
if (GPU_N > MAX_GPU_COUNT) {
GPU_N = MAX_GPU_COUNT;
int deviceCount = 0;
cudaGetDeviceCount(&deviceCount);
printf("CUDA-capable device count: %i\n", deviceCount);
// This sample needs at least two GPUs to show work running on several devices at once
if (deviceCount < 2) {
printf("Two or more CUDA-capable devices are required. Exiting.\n");
return EXIT_SUCCESS;
}
printf("CUDA-capable device count: %i\n", GPU_N);
printf("Generating input data...\n\n");
std::vector<TGPUplan> plan(deviceCount);
std::vector<float> h_SumGPU(deviceCount); // one result per GPU
// Subdividing input data across GPUs
// Get data sizes for each GPU
for (i = 0; i < GPU_N; i++) {
plan[i].dataN = DATA_N / GPU_N;
for (int i = 0; i < deviceCount; i++) {
plan[i].dataN = DATA_N / deviceCount;
}
// Take into account "odd" data sizes
for (i = 0; i < DATA_N % GPU_N; i++) {
for (int i = 0; i < DATA_N % deviceCount; i++) {
plan[i].dataN++;
}
// Assign data ranges to GPUs
gpuBase = 0;
for (i = 0; i < GPU_N; i++) {
plan[i].h_Sum = h_SumGPU + i;
gpuBase += plan[i].dataN;
}
// Create streams for issuing GPU command asynchronously and allocate memory
// (GPU and System page-locked)
for (i = 0; i < GPU_N; i++) {
checkCudaErrors(cudaSetDevice(i));
checkCudaErrors(cudaStreamCreate(&plan[i].stream));
// Allocate memory
checkCudaErrors(cudaMalloc((void **)&plan[i].d_Data, plan[i].dataN * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&plan[i].d_Sum, ACCUM_N * sizeof(float)));
checkCudaErrors(cudaMallocHost((void **)&plan[i].h_Sum_from_device, ACCUM_N * sizeof(float)));
checkCudaErrors(cudaMallocHost((void **)&plan[i].h_Data, plan[i].dataN * sizeof(float)));
for (int i = 0; i < deviceCount; i++) {
cudaSetDevice(i);
cudaStreamCreate(&plan[i].stream);
for (j = 0; j < plan[i].dataN; j++) {
cudaMalloc(&plan[i].d_Data, plan[i].dataN * sizeof(float));
cudaMalloc(&plan[i].d_Sum, BLOCK_N * sizeof(float));
cudaMallocHost(&plan[i].h_Sum_from_device, BLOCK_N * sizeof(float));
cudaMallocHost(&plan[i].h_Data, plan[i].dataN * sizeof(float));
for (int j = 0; j < plan[i].dataN; j++) {
plan[i].h_Data[j] = (float)rand() / (float)RAND_MAX;
}
}
// Start timing and compute on GPU(s)
printf("Computing with %d GPUs...\n", GPU_N);
// create and start timer
StopWatchInterface *timer = NULL;
sdkCreateTimer(&timer);
printf("Computing with %d GPUs...\n", deviceCount);
// Time the multi-GPU work with CUDA events (recorded on device 0)
cudaEvent_t start, stop;
cudaSetDevice(0);
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
// start the timer
sdkStartTimer(&timer);
// Copy data to GPU, launch the kernel and copy data back. All asynchronously
for (i = 0; i < GPU_N; i++) {
// Set device
checkCudaErrors(cudaSetDevice(i));
// Queue every GPU's copy-in, kernel and copy-out before waiting on any of them,
// so the devices run concurrently instead of one after another
for (int i = 0; i < deviceCount; i++) {
TGPUplan &gpu = plan[i];
cudaSetDevice(i);
// Copy input data from CPU
checkCudaErrors(cudaMemcpyAsync(
plan[i].d_Data, plan[i].h_Data, plan[i].dataN * sizeof(float), cudaMemcpyHostToDevice, plan[i].stream));
cudaMemcpyAsync(gpu.d_Data, gpu.h_Data, gpu.dataN * sizeof(float), cudaMemcpyHostToDevice, gpu.stream);
// Perform GPU computations
reduceKernel<<<BLOCK_N, THREAD_N, 0, plan[i].stream>>>(plan[i].d_Sum, plan[i].d_Data, plan[i].dataN);
getLastCudaError("reduceKernel() execution failed.\n");
// Launch Kernel
reduceKernel<<<BLOCK_N, THREAD_N, 0, gpu.stream>>>(gpu.d_Sum, gpu.d_Data, gpu.dataN);
// Read back GPU results
checkCudaErrors(cudaMemcpyAsync(
plan[i].h_Sum_from_device, plan[i].d_Sum, ACCUM_N * sizeof(float), cudaMemcpyDeviceToHost, plan[i].stream));
cudaMemcpyAsync(gpu.h_Sum_from_device, gpu.d_Sum, BLOCK_N * sizeof(float), cudaMemcpyDeviceToHost, gpu.stream);
}
// Process GPU results
for (i = 0; i < GPU_N; i++) {
float sum;
// Set device
checkCudaErrors(cudaSetDevice(i));
for (int i = 0; i < deviceCount; i++) {
cudaSetDevice(i);
// Wait for all operations to finish
cudaStreamSynchronize(plan[i].stream);
// Finalize GPU reduction for current subvector
sum = 0;
float sum = 0;
for (j = 0; j < ACCUM_N; j++) {
for (int j = 0; j < BLOCK_N; j++) {
sum += plan[i].h_Sum_from_device[j];
}
*(plan[i].h_Sum) = (float)sum;
h_SumGPU[i] = sum;
// Shut down this GPU
checkCudaErrors(cudaFreeHost(plan[i].h_Sum_from_device));
checkCudaErrors(cudaFree(plan[i].d_Sum));
checkCudaErrors(cudaFree(plan[i].d_Data));
checkCudaErrors(cudaStreamDestroy(plan[i].stream));
// Free up this GPU's resources. h_Data stays alive for the CPU check below.
cudaFreeHost(plan[i].h_Sum_from_device);
cudaFree(plan[i].d_Sum);
cudaFree(plan[i].d_Data);
cudaStreamDestroy(plan[i].stream);
}
sumGPU = 0;
float sumGPU = 0;
for (i = 0; i < GPU_N; i++) {
for (int i = 0; i < deviceCount; i++) {
sumGPU += h_SumGPU[i];
}
sdkStopTimer(&timer);
printf(" GPU Processing time: %f (ms)\n\n", sdkGetTimerValue(&timer));
sdkDeleteTimer(&timer);
cudaSetDevice(0);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float gpuTime = 0;
cudaEventElapsedTime(&gpuTime, start, stop);
printf(" GPU Processing time: %f (ms)\n\n", gpuTime);
cudaEventDestroy(start);
cudaEventDestroy(stop);
// Compute on Host CPU
printf("Computing with Host CPU...\n\n");
sumCPU = 0;
double sumCPU = 0;
for (i = 0; i < GPU_N; i++) {
for (j = 0; j < plan[i].dataN; j++) {
for (int i = 0; i < deviceCount; i++) {
for (int j = 0; j < plan[i].dataN; j++) {
sumCPU += plan[i].h_Data[j];
}
}
// Compare GPU and CPU results
printf("Comparing GPU and Host CPU results...\n");
diff = fabs(sumCPU - sumGPU) / fabs(sumCPU);
const double diff = fabs(sumCPU - sumGPU) / fabs(sumCPU);
printf(" GPU sum: %f\n CPU sum: %f\n", sumGPU, sumCPU);
printf(" Relative difference: %E \n\n", diff);
// Cleanup and shutdown
for (i = 0; i < GPU_N; i++) {
checkCudaErrors(cudaSetDevice(i));
checkCudaErrors(cudaFreeHost(plan[i].h_Data));
for (int i = 0; i < deviceCount; i++) {
cudaFreeHost(plan[i].h_Data);
}
exit((diff < 1e-5) ? EXIT_SUCCESS : EXIT_FAILURE);
return (diff < 1e-5) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@@ -1,62 +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 application demonstrates how to use the CUDA API to use multiple GPUs.
*
* Note that in order to detect multiple GPUs in your system you have to disable
* SLI in the nvidia control panel. Otherwise only one GPU is visible to the
* application. On the other side, you can still extend your desktop to screens
* attached to both GPUs.
*/
#ifndef SIMPLEMULTIGPU_H
#define SIMPLEMULTIGPU_H
typedef struct
{
// Host-side input data
int dataN;
float *h_Data;
// Partial sum for this GPU
float *h_Sum;
// Device buffers
float *d_Data, *d_Sum;
// Reduction copied back from GPU
float *h_Sum_from_device;
// Stream for asynchronous command execution
cudaStream_t stream;
} TGPUplan;
extern "C" void launch_reduceKernel(float *d_Result, float *d_Input, int N, int BLOCK_N, int THREAD_N, cudaStream_t &s);
#endif