[CCCL] 瘦身 + 补全: 移除 cudax/python/libcudacxx-tests 冗余文件, 新增 c2h 测试助手 + cmake 构建系统 + 8 个 CUDA thrust examples
变更摘要:
- 删除: cudax/ (783 files, 7.2M) — 实验性组件,竞赛不需要
- 删除: python/ (226 files, 2.0M) — Python 绑定,竞赛不需要
- 删除: libcudacxx/{test,benchmarks,codegen,cmake,share} (4432 files, 31M)
保留: libcudacxx/include/ (1463 headers, cuda::std 编译依赖)
- 新增: c2h/ (27 files) — CUB Catch2 测试辅助头文件,编译 243 个测试必需
- 新增: cmake/ (29 files) — CCCL 原生 CMake 构建系统
- 新增: thrust/examples/cuda/ (7 files) + cpp_integration/ (1 file)
async_reduce, custom_temporary_allocation, explicit_cuda_stream,
global_device_vector, range_view, unwrap_pointer, wrap_pointer, device
结果: cccl_upstream 从 74M→35M (瘦身 53%), 核心内容 100% 保留:
27/27 tuning headers, 78 benchmarks, 243 tests,
60 thrust examples, 18 CUB examples, 全部编译头文件
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
# vectorAdd - Vector Addition
|
||||
|
||||
## Description
|
||||
|
||||
This CUDA Runtime API sample is a very basic sample that implements element by element vector addition. It is the same as the sample illustrating Chapter 3 of the programming guide with some additions like error checking.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CUDA Runtime API, Vector Addition
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 3.5 ](https://developer.nvidia.com/cuda-gpus) [SM 3.7 ](https://developer.nvidia.com/cuda-gpus) [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)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, ppc64le, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaFree, cudaMalloc, cudaGetLastError, cudaMemcpy, cudaGetErrorString
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit 11.6](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## Build and Run
|
||||
|
||||
### Windows
|
||||
The Windows samples are built using the Visual Studio IDE. Solution files (.sln) are provided for each supported version of Visual Studio, using the format:
|
||||
```
|
||||
*_vs<version>.sln - for Visual Studio <version>
|
||||
```
|
||||
Each individual sample has its own set of solution files in its directory:
|
||||
|
||||
To build/examine all the samples at once, the complete solution files should be used. To build/examine a single sample, the individual sample solution files should be used.
|
||||
> **Note:** Some samples require that the Microsoft DirectX SDK (June 2010 or newer) be installed and that the VC++ directory paths are properly set up (**Tools > Options...**). Check DirectX Dependencies section for details."
|
||||
|
||||
### Linux
|
||||
The Linux samples are built using makefiles. To use the makefiles, change the current directory to the sample directory you wish to build, and run make:
|
||||
```
|
||||
$ cd <sample_dir>
|
||||
$ make
|
||||
```
|
||||
The samples makefiles can take advantage of certain options:
|
||||
* **TARGET_ARCH=<arch>** - cross-compile targeting a specific architecture. Allowed architectures are x86_64, ppc64le, armv7l.
|
||||
By default, TARGET_ARCH is set to HOST_ARCH. On a x86_64 machine, not setting TARGET_ARCH is the equivalent of setting TARGET_ARCH=x86_64.<br/>
|
||||
`$ make TARGET_ARCH=x86_64` <br/> `$ make TARGET_ARCH=ppc64le` <br/> `$ make TARGET_ARCH=armv7l` <br/>
|
||||
See [here](http://docs.nvidia.com/cuda/cuda-samples/index.html#cross-samples) for more details.
|
||||
* **dbg=1** - build with debug symbols
|
||||
```
|
||||
$ make dbg=1
|
||||
```
|
||||
* **SMS="A B ..."** - override the SM architectures for which the sample will be built, where `"A B ..."` is a space-delimited list of SM architectures. For example, to generate SASS for SM 50 and SM 60, use `SMS="50 60"`.
|
||||
```
|
||||
$ make SMS="50 60"
|
||||
```
|
||||
|
||||
* **HOST_COMPILER=<host_compiler>** - override the default g++ host compiler. See the [Linux Installation Guide](http://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#system-requirements) for a list of supported host compilers.
|
||||
```
|
||||
$ make HOST_COMPILER=g++
|
||||
```
|
||||
|
||||
## References (for more details)
|
||||
@@ -1,44 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Vector addition: C = A + B.
|
||||
*
|
||||
* This sample is a very basic sample that implements element by element
|
||||
* vector addition. It is the same as the sample illustrating Chapter 2
|
||||
* of the programming guide with some additions like error checking.
|
||||
*/
|
||||
@@ -1,149 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Vector addition: C = A + B.
|
||||
*
|
||||
* This sample is a very basic sample that implements element by element
|
||||
* vector addition. It is the same as the sample illustrating Chapter 2
|
||||
* of the programming guide with some additions like error checking.
|
||||
*/
|
||||
|
||||
#include <cuda/experimental/__stf/graph/graph_ctx.cuh>
|
||||
#include <cuda/experimental/__stf/stream/stream_ctx.cuh>
|
||||
|
||||
using namespace cuda::experimental::stf;
|
||||
|
||||
/**
|
||||
* CUDA Kernel Device code
|
||||
*
|
||||
* Computes the vector addition of A and B into C. The 3 vectors have the same
|
||||
* number of elements numElements.
|
||||
*/
|
||||
__global__ void vectorAdd(const float* A, const float* B, float* C, int numElements)
|
||||
{
|
||||
int i = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
|
||||
if (i < numElements)
|
||||
{
|
||||
C[i] = A[i] + B[i] + 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Ctx>
|
||||
void run()
|
||||
{
|
||||
Ctx ctx;
|
||||
// Error code to check return values for CUDA calls
|
||||
cudaError_t err = cudaSuccess;
|
||||
|
||||
// Print the vector length to be used, and compute its size
|
||||
int numElements = 50000;
|
||||
size_t size = numElements * sizeof(float);
|
||||
// printf("[Vector addition of %d elements]\n", numElements);
|
||||
|
||||
// Allocate the host input vector A
|
||||
float* h_A = (float*) malloc(size);
|
||||
|
||||
// Allocate the host input vector B
|
||||
float* h_B = (float*) malloc(size);
|
||||
|
||||
// Allocate the host output vector C
|
||||
float* h_C = (float*) malloc(size);
|
||||
|
||||
// Verify that allocations succeeded
|
||||
if (h_A == NULL || h_B == NULL || h_C == NULL)
|
||||
{
|
||||
fprintf(stderr, "Failed to allocate host vectors!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Initialize the host input vectors
|
||||
for (int i = 0; i < numElements; ++i)
|
||||
{
|
||||
h_A[i] = rand() / (float) RAND_MAX;
|
||||
h_B[i] = rand() / (float) RAND_MAX;
|
||||
}
|
||||
|
||||
auto A_handle = ctx.logical_data(h_A, numElements);
|
||||
auto B_handle = ctx.logical_data(h_B, numElements);
|
||||
auto C_handle = ctx.logical_data(h_C, numElements);
|
||||
|
||||
ctx.task(A_handle.read(), B_handle.read(), C_handle.write())->*[&](cudaStream_t stream, auto d_A, auto d_B, auto d_C) {
|
||||
// Launch the Vector Add CUDA Kernel
|
||||
int threadsPerBlock = 256;
|
||||
int blocksPerGrid = (numElements + threadsPerBlock - 1) / threadsPerBlock;
|
||||
// printf("CUDA kernel launch with %d blocks of %d threads\n", blocksPerGrid, threadsPerBlock);
|
||||
vectorAdd<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(
|
||||
d_A.data_handle(), d_B.data_handle(), d_C.data_handle(), numElements);
|
||||
err = cudaGetLastError();
|
||||
|
||||
if (err != cudaSuccess)
|
||||
{
|
||||
fprintf(stderr, "Failed to launch vectorAdd kernel (error code %s)!\n", cudaGetErrorString(err));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
};
|
||||
|
||||
ctx.host_launch(A_handle.read(), B_handle.read(), C_handle.read())->*[](auto hA, auto hB, auto hC) {
|
||||
// Verify that the result vector is correct
|
||||
for (int i = 0; i < hC.extent(0); ++i)
|
||||
{
|
||||
if (::std::abs(hA[i] + hB[i] - hC[i]) > 1e-5)
|
||||
{
|
||||
fprintf(stderr, "Result verification failed at element %d!\n", i);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ctx.finalize();
|
||||
|
||||
// Free host memory
|
||||
free(h_A);
|
||||
free(h_B);
|
||||
free(h_C);
|
||||
}
|
||||
|
||||
/**
|
||||
* Host main routine
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
run<stream_ctx>();
|
||||
run<graph_ctx>();
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
/* Copyright (c) 2023, 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 file demonstrates the usage of conditional graph nodes with
|
||||
* a series of *simple* example graphs.
|
||||
*
|
||||
* For more information on conditional nodes, see the programming guide:
|
||||
*
|
||||
* https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#conditional-graph-nodes
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cuda/experimental/stf.cuh>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
using namespace cuda::experimental::stf;
|
||||
|
||||
#if _CCCL_CTK_AT_LEAST(12, 4)
|
||||
|
||||
// This kernel will only be executed if the condition is true
|
||||
__global__ void doWhileEmptyKernel(void)
|
||||
{
|
||||
printf("GPU: doWhileEmptyKernel()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
__global__ void doWhileLoopKernel(char* dPtr, cudaGraphConditionalHandle handle)
|
||||
{
|
||||
if (--(*dPtr) == 0)
|
||||
{
|
||||
cudaGraphSetConditional(handle, 0);
|
||||
}
|
||||
printf("GPU: counter = %d\n", *dPtr);
|
||||
}
|
||||
|
||||
void simpleDoWhileGraph(void)
|
||||
{
|
||||
cudaGraph_t graph;
|
||||
cudaGraphExec_t graphExec;
|
||||
cudaGraphNode_t conditionalNode;
|
||||
|
||||
// Allocate a byte of device memory to use as input
|
||||
char* dPtr;
|
||||
cuda_safe_call(cudaMalloc((void**) &dPtr, 1));
|
||||
|
||||
printf("simpleDoWhileGraph: Building graph...\n");
|
||||
cuda_safe_call(cudaGraphCreate(&graph, 0));
|
||||
|
||||
cudaGraphConditionalHandle handle;
|
||||
cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault));
|
||||
|
||||
cudaGraphNodeParams cParams{};
|
||||
cParams.type = cudaGraphNodeTypeConditional;
|
||||
cParams.conditional.handle = handle;
|
||||
cParams.conditional.type = cudaGraphCondTypeWhile;
|
||||
cParams.conditional.size = 1;
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, NULL, NULL, 0, &cParams));
|
||||
# else
|
||||
cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, NULL, 0, &cParams));
|
||||
# endif
|
||||
|
||||
cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];
|
||||
|
||||
cudaStream_t captureStream;
|
||||
cuda_safe_call(cudaStreamCreate(&captureStream));
|
||||
|
||||
cuda_safe_call(
|
||||
cudaStreamBeginCaptureToGraph(captureStream, bodyGraph, nullptr, nullptr, 0, cudaStreamCaptureModeGlobal));
|
||||
doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();
|
||||
doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();
|
||||
doWhileLoopKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);
|
||||
cuda_safe_call(cudaStreamEndCapture(captureStream, nullptr));
|
||||
cuda_safe_call(cudaStreamDestroy(captureStream));
|
||||
|
||||
cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
|
||||
|
||||
// Initialize device memory and launch the graph
|
||||
cuda_safe_call(cudaMemset(dPtr, 10, 1)); // Set dPtr to 10
|
||||
printf("Host: Launching graph with loop counter set to 10\n");
|
||||
cuda_safe_call(cudaGraphLaunch(graphExec, 0));
|
||||
cuda_safe_call(cudaDeviceSynchronize());
|
||||
|
||||
// Cleanup
|
||||
cuda_safe_call(cudaGraphExecDestroy(graphExec));
|
||||
cuda_safe_call(cudaGraphDestroy(graph));
|
||||
cuda_safe_call(cudaFree(dPtr));
|
||||
|
||||
printf("simpleDoWhileGraph: Complete\n\n");
|
||||
}
|
||||
|
||||
void stf_dowhile()
|
||||
{
|
||||
stackable_ctx ctx;
|
||||
|
||||
{
|
||||
auto repeat_guard = ctx.repeat_graph_scope(10);
|
||||
|
||||
ctx.task()->*[](cudaStream_t stream) {
|
||||
doWhileEmptyKernel<<<1, 1, 0, stream>>>();
|
||||
doWhileEmptyKernel<<<1, 1, 0, stream>>>();
|
||||
};
|
||||
}
|
||||
|
||||
ctx.finalize();
|
||||
printf("STF do while complete\n\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a graph containing a conditional while loop using stream capture.
|
||||
* This demonstrates how to insert a conditional node into a stream which is
|
||||
* being captured. The graph consists of a kernel node, A, followed by a
|
||||
* conditional while node, B, followed by a kernel node, D. The conditional
|
||||
* body is populated by a single kernel node, C:
|
||||
*
|
||||
* A -> B [ C ] -> D
|
||||
*
|
||||
* The same kernel will be used for both nodes A and C. This kernel will test
|
||||
* a device memory location and set the condition when the location is non-zero.
|
||||
* We must run the kernel before the loop as well as inside the loop in order
|
||||
* to behave like a while loop as opposed to a do-while loop. We need to evaluate
|
||||
* the device memory location before the conditional node is evaluated in order
|
||||
* to set the condition variable properly. Because we're using a kernel upstream
|
||||
* of the conditional node, there is no need to use the handle default value to
|
||||
* initialize the conditional value.
|
||||
*/
|
||||
|
||||
__global__ void capturedWhileKernel(char* dPtr, cudaGraphConditionalHandle handle)
|
||||
{
|
||||
printf("GPU: counter = %d\n", *dPtr);
|
||||
if (*dPtr)
|
||||
{
|
||||
(*dPtr)--;
|
||||
}
|
||||
cudaGraphSetConditional(handle, *dPtr);
|
||||
}
|
||||
|
||||
__global__ void capturedWhileEmptyKernel(void)
|
||||
{
|
||||
printf("GPU: capturedWhileEmptyKernel()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void capturedWhileGraph(void)
|
||||
{
|
||||
cudaGraph_t graph;
|
||||
cudaGraphExec_t graphExec;
|
||||
|
||||
cudaStreamCaptureStatus status;
|
||||
const cudaGraphNode_t* dependencies;
|
||||
size_t numDependencies;
|
||||
|
||||
// Allocate a byte of device memory to use as input
|
||||
char* dPtr;
|
||||
cuda_safe_call(cudaMalloc((void**) &dPtr, 1));
|
||||
|
||||
printf("capturedWhileGraph: Building graph...\n");
|
||||
cudaStream_t captureStream;
|
||||
cuda_safe_call(cudaStreamCreate(&captureStream));
|
||||
|
||||
cuda_safe_call(cudaStreamBeginCapture(captureStream, cudaStreamCaptureModeGlobal));
|
||||
|
||||
// Obtain the handle of the graph
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));
|
||||
# else
|
||||
cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, &numDependencies));
|
||||
# endif
|
||||
|
||||
// Create the conditional handle
|
||||
cudaGraphConditionalHandle handle;
|
||||
cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, graph));
|
||||
|
||||
// Insert kernel node A
|
||||
capturedWhileKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);
|
||||
|
||||
// Obtain the handle for node A
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));
|
||||
# else
|
||||
cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, &numDependencies));
|
||||
# endif
|
||||
|
||||
// Insert conditional node B
|
||||
cudaGraphNode_t conditionalNode;
|
||||
cudaGraphNodeParams cParams{};
|
||||
cParams.type = cudaGraphNodeTypeConditional;
|
||||
cParams.conditional.handle = handle;
|
||||
cParams.conditional.type = cudaGraphCondTypeWhile;
|
||||
cParams.conditional.size = 1;
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, dependencies, NULL, numDependencies, &cParams));
|
||||
# else
|
||||
cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, dependencies, numDependencies, &cParams));
|
||||
# endif
|
||||
|
||||
cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];
|
||||
|
||||
// Update stream capture dependencies to account for the node we manually added
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
cuda_safe_call(
|
||||
cudaStreamUpdateCaptureDependencies(captureStream, &conditionalNode, NULL, 1, cudaStreamSetCaptureDependencies));
|
||||
# else
|
||||
cuda_safe_call(
|
||||
cudaStreamUpdateCaptureDependencies(captureStream, &conditionalNode, 1, cudaStreamSetCaptureDependencies));
|
||||
# endif
|
||||
|
||||
// Insert kernel node D
|
||||
capturedWhileEmptyKernel<<<1, 1, 0, captureStream>>>();
|
||||
|
||||
cuda_safe_call(cudaStreamEndCapture(captureStream, &graph));
|
||||
cuda_safe_call(cudaStreamDestroy(captureStream));
|
||||
|
||||
// Populate conditional body graph using stream capture
|
||||
cudaStream_t bodyStream;
|
||||
cuda_safe_call(cudaStreamCreate(&bodyStream));
|
||||
|
||||
cuda_safe_call(
|
||||
cudaStreamBeginCaptureToGraph(bodyStream, bodyGraph, nullptr, nullptr, 0, cudaStreamCaptureModeGlobal));
|
||||
|
||||
// Insert kernel node C
|
||||
capturedWhileKernel<<<1, 1, 0, bodyStream>>>(dPtr, handle);
|
||||
cuda_safe_call(cudaStreamEndCapture(bodyStream, nullptr));
|
||||
cuda_safe_call(cudaStreamDestroy(bodyStream));
|
||||
|
||||
cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
|
||||
|
||||
// Initialize device memory and launch the graph
|
||||
// Device memory is zero, so the conditional node will not execute
|
||||
cuda_safe_call(cudaMemset(dPtr, 0, 1)); // Set dPtr to 0
|
||||
printf("Host: Launching graph with loop counter set to 0\n");
|
||||
cuda_safe_call(cudaGraphLaunch(graphExec, 0));
|
||||
cuda_safe_call(cudaDeviceSynchronize());
|
||||
|
||||
// Initialize device memory and launch the graph
|
||||
cuda_safe_call(cudaMemset(dPtr, 10, 1)); // Set dPtr to 10
|
||||
printf("Host: Launching graph with loop counter set to 10\n");
|
||||
cuda_safe_call(cudaGraphLaunch(graphExec, 0));
|
||||
cuda_safe_call(cudaDeviceSynchronize());
|
||||
|
||||
// Cleanup
|
||||
cuda_safe_call(cudaGraphExecDestroy(graphExec));
|
||||
cuda_safe_call(cudaGraphDestroy(graph));
|
||||
cuda_safe_call(cudaFree(dPtr));
|
||||
|
||||
printf("capturedWhileGraph: Complete\n\n");
|
||||
}
|
||||
|
||||
void stf_dowhile_2()
|
||||
{
|
||||
stackable_ctx ctx;
|
||||
|
||||
{
|
||||
// We force everything to be a CUDA graph
|
||||
auto scope = ctx.graph_scope();
|
||||
|
||||
// We use a token to ensure that A, B(C) and D are serialized
|
||||
auto t = ctx.token();
|
||||
|
||||
// A
|
||||
ctx.task(t.rw())->*[](cudaStream_t stream) {
|
||||
doWhileEmptyKernel<<<1, 1, 0, stream>>>();
|
||||
};
|
||||
|
||||
// B
|
||||
{
|
||||
auto repeat_guard = ctx.repeat_graph_scope(10);
|
||||
|
||||
// C
|
||||
ctx.task(t.rw())->*[](cudaStream_t stream) {
|
||||
doWhileEmptyKernel<<<1, 1, 0, stream>>>();
|
||||
};
|
||||
}
|
||||
|
||||
// D
|
||||
ctx.task(t.rw())->*[](cudaStream_t stream) {
|
||||
doWhileEmptyKernel<<<1, 1, 0, stream>>>();
|
||||
};
|
||||
}
|
||||
|
||||
ctx.finalize();
|
||||
}
|
||||
|
||||
void stf_dowhile_2_cuda_kernel()
|
||||
{
|
||||
stackable_ctx ctx;
|
||||
|
||||
{
|
||||
// We force everything to be a CUDA graph
|
||||
auto scope = ctx.graph_scope();
|
||||
|
||||
// We use a token to ensure that A, B(C) and D are serialized
|
||||
auto t = ctx.token();
|
||||
|
||||
// A
|
||||
ctx.cuda_kernel(t.rw())->*[]() {
|
||||
return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};
|
||||
};
|
||||
|
||||
// B
|
||||
{
|
||||
auto repeat_guard = ctx.repeat_graph_scope(10);
|
||||
|
||||
// C
|
||||
ctx.cuda_kernel(t.rw())->*[]() {
|
||||
return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};
|
||||
};
|
||||
}
|
||||
|
||||
// D
|
||||
ctx.cuda_kernel(t.rw())->*[]() {
|
||||
return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};
|
||||
};
|
||||
}
|
||||
|
||||
ctx.finalize();
|
||||
}
|
||||
|
||||
#endif // _CCCL_CTK_AT_LEAST(12, 4)
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
#if _CCCL_CTK_BELOW(12, 4)
|
||||
fprintf(stderr, "Waiving test: conditional nodes are only available since CUDA 12.4.\n");
|
||||
return 0;
|
||||
#else
|
||||
simpleDoWhileGraph();
|
||||
stf_dowhile();
|
||||
|
||||
capturedWhileGraph();
|
||||
stf_dowhile_2();
|
||||
|
||||
// same as stf_dowhile_2 but uses cuda_kernel
|
||||
stf_dowhile_2_cuda_kernel();
|
||||
|
||||
return 0;
|
||||
#endif // _CCCL_CTK_AT_LEAST(12, 4)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
# jacobiCudaGraphs - Jacobi CUDA Graphs
|
||||
|
||||
## Description
|
||||
|
||||
Demonstrates Instantiated CUDA Graph Update with Jacobi Iterative Method using cudaGraphExecKernelNodeSetParams() and cudaGraphExecUpdate() approach.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CUDA Graphs, Stream Capture, Instantiated CUDA Graph Update, Cooperative Groups
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 3.5 ](https://developer.nvidia.com/cuda-gpus) [SM 3.7 ](https://developer.nvidia.com/cuda-gpus) [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)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, ppc64le, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaGraphAddMemsetNode, cudaStreamCreateWithFlags, cudaMemcpyAsync, cudaMallocHost, cudaPitchedPtr, cudaGraphCreate, cudaMalloc, cudaPos, cudaGraphAddMemcpyNode, cudaStreamEndCapture, cudaGraphExecDestroy, cudaStreamBeginCapture, cudaGraphExecKernelNodeSetParams, cudaStreamSynchronize, cudaGraphLaunch, cudaFree, cudaGraphInstantiate, cudaExtent, cudaMemsetAsync, cudaFreeHost, cudaGraphAddKernelNode, cudaGraphExecUpdate
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit 11.6](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
|
||||
## Build and Run
|
||||
|
||||
### Windows
|
||||
The Windows samples are built using the Visual Studio IDE. Solution files (.sln) are provided for each supported version of Visual Studio, using the format:
|
||||
```
|
||||
*_vs<version>.sln - for Visual Studio <version>
|
||||
```
|
||||
Each individual sample has its own set of solution files in its directory:
|
||||
|
||||
To build/examine all the samples at once, the complete solution files should be used. To build/examine a single sample, the individual sample solution files should be used.
|
||||
> **Note:** Some samples require that the Microsoft DirectX SDK (June 2010 or newer) be installed and that the VC++ directory paths are properly set up (**Tools > Options...**). Check DirectX Dependencies section for details."
|
||||
|
||||
### Linux
|
||||
The Linux samples are built using makefiles. To use the makefiles, change the current directory to the sample directory you wish to build, and run make:
|
||||
```
|
||||
$ cd <sample_dir>
|
||||
$ make
|
||||
```
|
||||
The samples makefiles can take advantage of certain options:
|
||||
* **TARGET_ARCH=<arch>** - cross-compile targeting a specific architecture. Allowed architectures are x86_64, ppc64le, armv7l.
|
||||
By default, TARGET_ARCH is set to HOST_ARCH. On a x86_64 machine, not setting TARGET_ARCH is the equivalent of setting TARGET_ARCH=x86_64.<br/>
|
||||
`$ make TARGET_ARCH=x86_64` <br/> `$ make TARGET_ARCH=ppc64le` <br/> `$ make TARGET_ARCH=armv7l` <br/>
|
||||
See [here](http://docs.nvidia.com/cuda/cuda-samples/index.html#cross-samples) for more details.
|
||||
* **dbg=1** - build with debug symbols
|
||||
```
|
||||
$ make dbg=1
|
||||
```
|
||||
* **SMS="A B ..."** - override the SM architectures for which the sample will be built, where `"A B ..."` is a space-delimited list of SM architectures. For example, to generate SASS for SM 50 and SM 60, use `SMS="50 60"`.
|
||||
```
|
||||
$ make SMS="50 60"
|
||||
```
|
||||
|
||||
* **HOST_COMPILER=<host_compiler>** - override the default g++ host compiler. See the [Linux Installation Guide](http://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#system-requirements) for a list of supported host compilers.
|
||||
```
|
||||
$ make HOST_COMPILER=g++
|
||||
```
|
||||
|
||||
## References (for more details)
|
||||
@@ -1,638 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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 sample demonstrates Instantiated CUDA Graph Update
|
||||
// with Jacobi Iterative Method in 3 different methods:
|
||||
// 1 - JacobiMethodGpuCudaGraphExecKernelSetParams() - CUDA Graph with
|
||||
// cudaGraphExecKernelNodeSetParams() 2 - JacobiMethodGpuCudaGraphExecUpdate() -
|
||||
// CUDA Graph with cudaGraphExecUpdate() 3 - JacobiMethodGpu() - Non CUDA Graph
|
||||
// method
|
||||
|
||||
// Jacobi method on a linear system A*x = b,
|
||||
// where A is diagonally dominant and the exact solution consists
|
||||
// of all ones.
|
||||
|
||||
#include <cuda/experimental/__stf/utility/cuda_safe_call.cuh>
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
using cuda::experimental::stf::cuda_safe_call;
|
||||
|
||||
#define N_ROWS 512
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
// 8 Rows of square-matrix A processed by each CTA.
|
||||
// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).
|
||||
#define ROWS_PER_CTA 8
|
||||
|
||||
#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600
|
||||
#else
|
||||
__device__ double atomicAdd(double* address, double val)
|
||||
{
|
||||
unsigned long long int* address_as_ull = (unsigned long long int*) address;
|
||||
unsigned long long int old = *address_as_ull, assumed;
|
||||
|
||||
do
|
||||
{
|
||||
assumed = old;
|
||||
old = atomicCAS(address_as_ull, assumed, __double_as_longlong(val + __longlong_as_double(assumed)));
|
||||
|
||||
// Note: uses integer comparison to avoid hang in case of NaN (since NaN !=
|
||||
// NaN)
|
||||
} while (assumed != old);
|
||||
|
||||
return __longlong_as_double(old);
|
||||
}
|
||||
#endif
|
||||
|
||||
// creates N_ROWS x N_ROWS matrix A with N_ROWS+1 on the diagonal and 1
|
||||
// elsewhere. The elements of the right hand side b all equal 2*n, hence the
|
||||
// exact solution x to A*x = b is a vector of ones.
|
||||
void createLinearSystem(float* A, double* b)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
b[i] = 2.0 * N_ROWS;
|
||||
for (j = 0; j < N_ROWS; j++)
|
||||
{
|
||||
A[i * N_ROWS + j] = 1.0;
|
||||
}
|
||||
A[i * N_ROWS + i] = N_ROWS + 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
static __global__ void
|
||||
JacobiMethod(const float* A, const double* b, const float conv_threshold, double* x, double* x_new, double* sum)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ double x_shared[N_ROWS]; // N_ROWS == n
|
||||
__shared__ double b_shared[ROWS_PER_CTA + 1];
|
||||
|
||||
for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x)
|
||||
{
|
||||
x_shared[i] = x[i];
|
||||
}
|
||||
|
||||
if (threadIdx.x < ROWS_PER_CTA)
|
||||
{
|
||||
int k = threadIdx.x;
|
||||
for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);
|
||||
k += ROWS_PER_CTA, i += ROWS_PER_CTA)
|
||||
{
|
||||
b_shared[i % (ROWS_PER_CTA + 1)] = b[i];
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
|
||||
for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++)
|
||||
{
|
||||
double rowThreadSum = 0.0;
|
||||
for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x)
|
||||
{
|
||||
rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);
|
||||
}
|
||||
|
||||
for (int offset = tile32.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
rowThreadSum += tile32.shfl_down(rowThreadSum, offset);
|
||||
}
|
||||
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
if (threadIdx.x < ROWS_PER_CTA)
|
||||
{
|
||||
cg::thread_block_tile<ROWS_PER_CTA> tile8 = cg::tiled_partition<ROWS_PER_CTA>(cta);
|
||||
double temp_sum = 0.0;
|
||||
|
||||
int k = threadIdx.x;
|
||||
|
||||
for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);
|
||||
k += ROWS_PER_CTA, i += ROWS_PER_CTA)
|
||||
{
|
||||
double dx = b_shared[i % (ROWS_PER_CTA + 1)];
|
||||
dx /= A[i * N_ROWS + i];
|
||||
|
||||
x_new[i] = (x_shared[i] + dx);
|
||||
temp_sum += fabs(dx);
|
||||
}
|
||||
|
||||
for (int offset = tile8.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
temp_sum += tile8.shfl_down(temp_sum, offset);
|
||||
}
|
||||
|
||||
if (tile8.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(sum, temp_sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Thread block size for finalError kernel should be multiple of 32
|
||||
static __global__ void finalError(double* x, double* g_sum)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
extern __shared__ double warpSum[];
|
||||
double sum = 0.0;
|
||||
|
||||
int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x)
|
||||
{
|
||||
double d = x[i] - 1.0;
|
||||
sum += fabs(d);
|
||||
}
|
||||
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
|
||||
for (int offset = tile32.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
sum += tile32.shfl_down(sum, offset);
|
||||
}
|
||||
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
warpSum[threadIdx.x / warpSize] = sum;
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
double blockSum = 0.0;
|
||||
if (threadIdx.x < (blockDim.x / warpSize))
|
||||
{
|
||||
blockSum = warpSum[threadIdx.x];
|
||||
}
|
||||
|
||||
if (threadIdx.x < 32)
|
||||
{
|
||||
for (int offset = tile32.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
blockSum += tile32.shfl_down(blockSum, offset);
|
||||
}
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(g_sum, blockSum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the Jacobi method for A*x = b on GPU with CUDA Graph -
|
||||
// cudaGraphExecKernelNodeSetParams().
|
||||
double JacobiMethodGpuCudaGraphExecKernelSetParams(
|
||||
const float* A,
|
||||
const double* b,
|
||||
float conv_threshold,
|
||||
const int max_iter,
|
||||
double* x,
|
||||
double* x_new,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
// CTA size
|
||||
dim3 nthreads(256, 1, 1);
|
||||
// grid size
|
||||
dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);
|
||||
cudaGraph_t graph;
|
||||
cudaGraphExec_t graphExec = NULL;
|
||||
|
||||
double sum = 0.0;
|
||||
double* d_sum = NULL;
|
||||
cuda_safe_call(cudaMalloc(&d_sum, sizeof(double)));
|
||||
|
||||
std::vector<cudaGraphNode_t> nodeDependencies;
|
||||
cudaGraphNode_t memcpyNode, jacobiKernelNode, memsetNode;
|
||||
cudaMemcpy3DParms memcpyParams;
|
||||
cudaMemsetParams memsetParams;
|
||||
|
||||
memsetParams.dst = (void*) d_sum;
|
||||
memsetParams.value = 0;
|
||||
memsetParams.pitch = 0;
|
||||
// elementSize can be max 4 bytes, so we take sizeof(float) and width=2
|
||||
memsetParams.elementSize = sizeof(float);
|
||||
memsetParams.width = 2;
|
||||
memsetParams.height = 1;
|
||||
|
||||
cuda_safe_call(cudaGraphCreate(&graph, 0));
|
||||
cuda_safe_call(cudaGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));
|
||||
nodeDependencies.push_back(memsetNode);
|
||||
|
||||
cudaKernelNodeParams NodeParams0, NodeParams1;
|
||||
NodeParams0.func = (void*) JacobiMethod;
|
||||
NodeParams0.gridDim = nblocks;
|
||||
NodeParams0.blockDim = nthreads;
|
||||
NodeParams0.sharedMemBytes = 0;
|
||||
void* kernelArgs0[6] = {
|
||||
(void*) &A,
|
||||
const_cast<double**>(&b),
|
||||
const_cast<float*>(&conv_threshold),
|
||||
(void*) &x,
|
||||
(void*) &x_new,
|
||||
(void*) &d_sum};
|
||||
NodeParams0.kernelParams = kernelArgs0;
|
||||
NodeParams0.extra = NULL;
|
||||
|
||||
cuda_safe_call(
|
||||
cudaGraphAddKernelNode(&jacobiKernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &NodeParams0));
|
||||
|
||||
nodeDependencies.clear();
|
||||
nodeDependencies.push_back(jacobiKernelNode);
|
||||
|
||||
memcpyParams.srcArray = NULL;
|
||||
memcpyParams.srcPos = make_cudaPos(0, 0, 0);
|
||||
memcpyParams.srcPtr = make_cudaPitchedPtr(d_sum, sizeof(double), 1, 1);
|
||||
memcpyParams.dstArray = NULL;
|
||||
memcpyParams.dstPos = make_cudaPos(0, 0, 0);
|
||||
memcpyParams.dstPtr = make_cudaPitchedPtr(&sum, sizeof(double), 1, 1);
|
||||
memcpyParams.extent = make_cudaExtent(sizeof(double), 1, 1);
|
||||
memcpyParams.kind = cudaMemcpyDeviceToHost;
|
||||
|
||||
cuda_safe_call(
|
||||
cudaGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));
|
||||
|
||||
cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
|
||||
|
||||
NodeParams1.func = (void*) JacobiMethod;
|
||||
NodeParams1.gridDim = nblocks;
|
||||
NodeParams1.blockDim = nthreads;
|
||||
NodeParams1.sharedMemBytes = 0;
|
||||
void* kernelArgs1[6] = {
|
||||
(void*) &A,
|
||||
const_cast<double**>(&b),
|
||||
const_cast<float*>(&conv_threshold),
|
||||
(void*) &x_new,
|
||||
(void*) &x,
|
||||
(void*) &d_sum};
|
||||
NodeParams1.kernelParams = kernelArgs1;
|
||||
NodeParams1.extra = NULL;
|
||||
|
||||
int k = 0;
|
||||
for (k = 0; k < max_iter; k++)
|
||||
{
|
||||
cuda_safe_call(
|
||||
cudaGraphExecKernelNodeSetParams(graphExec, jacobiKernelNode, ((k & 1) == 0) ? &NodeParams0 : &NodeParams1));
|
||||
cuda_safe_call(cudaGraphLaunch(graphExec, stream));
|
||||
cuda_safe_call(cudaStreamSynchronize(stream));
|
||||
|
||||
if (sum <= conv_threshold)
|
||||
{
|
||||
cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));
|
||||
nblocks.x = (N_ROWS / nthreads.x) + 1;
|
||||
size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);
|
||||
if ((k & 1) == 0)
|
||||
{
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);
|
||||
}
|
||||
|
||||
cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));
|
||||
cuda_safe_call(cudaStreamSynchronize(stream));
|
||||
// printf("GPU iterations : %d\n", k + 1);
|
||||
// printf("GPU error: %.3e\n", sum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cuda_safe_call(cudaFree(d_sum));
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Run the Jacobi method for A*x = b on GPU with Instantiated CUDA Graph Update
|
||||
// API - cudaGraphExecUpdate().
|
||||
double JacobiMethodGpuCudaGraphExecUpdate(
|
||||
const float* A,
|
||||
const double* b,
|
||||
const float conv_threshold,
|
||||
const int max_iter,
|
||||
double* x,
|
||||
double* x_new,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
// CTA size
|
||||
dim3 nthreads(256, 1, 1);
|
||||
// grid size
|
||||
dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);
|
||||
cudaGraph_t graph;
|
||||
cudaGraphExec_t graphExec = NULL;
|
||||
|
||||
double sum = 0.0;
|
||||
double* d_sum;
|
||||
cuda_safe_call(cudaMalloc(&d_sum, sizeof(double)));
|
||||
|
||||
int k = 0;
|
||||
for (k = 0; k < max_iter; k++)
|
||||
{
|
||||
cuda_safe_call(cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal));
|
||||
cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));
|
||||
if ((k & 1) == 0)
|
||||
{
|
||||
JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);
|
||||
}
|
||||
else
|
||||
{
|
||||
JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);
|
||||
}
|
||||
cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));
|
||||
cuda_safe_call(cudaStreamEndCapture(stream, &graph));
|
||||
|
||||
if (graphExec == NULL)
|
||||
{
|
||||
cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
cudaGraphExecUpdateResult updateResult_out;
|
||||
cuda_safe_call(cudaGraphExecUpdate(graphExec, graph, NULL, &updateResult_out));
|
||||
if (updateResult_out != cudaGraphExecUpdateSuccess)
|
||||
{
|
||||
if (graphExec != NULL)
|
||||
{
|
||||
cuda_safe_call(cudaGraphExecDestroy(graphExec));
|
||||
}
|
||||
printf("k = %d graph update failed with error - %d\n", k, updateResult_out);
|
||||
cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));
|
||||
}
|
||||
}
|
||||
cuda_safe_call(cudaGraphLaunch(graphExec, stream));
|
||||
cuda_safe_call(cudaStreamSynchronize(stream));
|
||||
|
||||
if (sum <= conv_threshold)
|
||||
{
|
||||
cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));
|
||||
nblocks.x = (N_ROWS / nthreads.x) + 1;
|
||||
size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);
|
||||
if ((k & 1) == 0)
|
||||
{
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);
|
||||
}
|
||||
|
||||
cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));
|
||||
cuda_safe_call(cudaStreamSynchronize(stream));
|
||||
// printf("GPU iterations : %d\n", k + 1);
|
||||
// printf("GPU error: %.3e\n", sum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cuda_safe_call(cudaFree(d_sum));
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Run the Jacobi method for A*x = b on GPU without CUDA Graph.
|
||||
double JacobiMethodGpu(
|
||||
const float* A,
|
||||
const double* b,
|
||||
const float conv_threshold,
|
||||
const int max_iter,
|
||||
double* x,
|
||||
double* x_new,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
// CTA size
|
||||
dim3 nthreads(256, 1, 1);
|
||||
// grid size
|
||||
dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);
|
||||
|
||||
double sum = 0.0;
|
||||
double* d_sum;
|
||||
cuda_safe_call(cudaMalloc(&d_sum, sizeof(double)));
|
||||
int k = 0;
|
||||
|
||||
for (k = 0; k < max_iter; k++)
|
||||
{
|
||||
cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));
|
||||
if ((k & 1) == 0)
|
||||
{
|
||||
JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);
|
||||
}
|
||||
else
|
||||
{
|
||||
JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);
|
||||
}
|
||||
cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));
|
||||
cuda_safe_call(cudaStreamSynchronize(stream));
|
||||
|
||||
if (sum <= conv_threshold)
|
||||
{
|
||||
cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));
|
||||
nblocks.x = (N_ROWS / nthreads.x) + 1;
|
||||
size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);
|
||||
if ((k & 1) == 0)
|
||||
{
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);
|
||||
}
|
||||
|
||||
cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));
|
||||
cuda_safe_call(cudaStreamSynchronize(stream));
|
||||
// printf("GPU iterations : %d\n", k + 1);
|
||||
// printf("GPU error: %.3e\n", sum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cuda_safe_call(cudaFree(d_sum));
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Run the Jacobi method for A*x = b on CPU.
|
||||
void JacobiMethodCPU(float* A, double* b, float conv_threshold, int max_iter, int* num_iter, double* x)
|
||||
{
|
||||
double* x_new;
|
||||
x_new = (double*) calloc(N_ROWS, sizeof(double));
|
||||
int k;
|
||||
|
||||
for (k = 0; k < max_iter; k++)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
double temp_dx = b[i];
|
||||
for (int j = 0; j < N_ROWS; j++)
|
||||
{
|
||||
temp_dx -= A[i * N_ROWS + j] * x[j];
|
||||
}
|
||||
temp_dx /= A[i * N_ROWS + i];
|
||||
x_new[i] += temp_dx;
|
||||
sum += fabs(temp_dx);
|
||||
}
|
||||
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
x[i] = x_new[i];
|
||||
}
|
||||
|
||||
if (sum <= conv_threshold)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
*num_iter = k + 1;
|
||||
free(x_new);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// if (checkCmdLineFlag(argc, (const char **)argv, "help")) {
|
||||
// printf("Command line: jacobiCudaGraphs [-option]\n");
|
||||
// printf("Valid options:\n");
|
||||
// printf(
|
||||
// "-gpumethod=<0,1 or 2> : 0 - [Default] "
|
||||
// "JacobiMethodGpuCudaGraphExecKernelSetParams\n");
|
||||
// printf(" : 1 - JacobiMethodGpuCudaGraphExecUpdate\n");
|
||||
// printf(" : 2 - JacobiMethodGpu - Non CUDA Graph\n");
|
||||
// printf("-device=device_num : cuda device id");
|
||||
// printf("-help : Output a help message\n");
|
||||
// exit(EXIT_SUCCESS);
|
||||
// }
|
||||
//
|
||||
int gpumethod = 0;
|
||||
// if (checkCmdLineFlag(argc, (const char **)argv, "gpumethod")) {
|
||||
// gpumethod = getCmdLineArgumentInt(argc, (const char **)argv, "gpumethod");
|
||||
//
|
||||
// if (gpumethod < 0 || gpumethod > 2) {
|
||||
// printf("Error: gpumethod must be 0 or 1 or 2, gpumethod=%d is invalid\n",
|
||||
// gpumethod);
|
||||
// exit(EXIT_SUCCESS);
|
||||
// }
|
||||
// }
|
||||
|
||||
// int dev = findCudaDevice(argc, (const char **)argv);
|
||||
// int dev = 0;
|
||||
|
||||
double* b = NULL;
|
||||
float* A = NULL;
|
||||
cuda_safe_call(cudaMallocHost(&b, N_ROWS * sizeof(double)));
|
||||
memset(b, 0, N_ROWS * sizeof(double));
|
||||
cuda_safe_call(cudaMallocHost(&A, N_ROWS * N_ROWS * sizeof(float)));
|
||||
memset(A, 0, N_ROWS * N_ROWS * sizeof(float));
|
||||
|
||||
createLinearSystem(A, b);
|
||||
double* x = NULL;
|
||||
// start with array of all zeroes
|
||||
x = (double*) calloc(N_ROWS, sizeof(double));
|
||||
|
||||
float conv_threshold = 1.0e-2;
|
||||
int max_iter = 4 * N_ROWS * N_ROWS;
|
||||
int cnt = 0;
|
||||
|
||||
// // create timer
|
||||
// StopWatchInterface *timerCPU = NULL, *timerGpu = NULL;
|
||||
// sdkCreateTimer(&timerCPU);
|
||||
//
|
||||
// sdkStartTimer(&timerCPU);
|
||||
JacobiMethodCPU(A, b, conv_threshold, max_iter, &cnt, x);
|
||||
|
||||
double sum = 0.0;
|
||||
// Compute error
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
double d = x[i] - 1.0;
|
||||
sum += fabs(d);
|
||||
}
|
||||
// sdkStopTimer(&timerCPU);
|
||||
// printf("CPU iterations : %d\n", cnt);
|
||||
// printf("CPU error: %.3e\n", sum);
|
||||
// printf("CPU Processing time: %f (ms)\n", sdkGetTimerValue(&timerCPU));
|
||||
|
||||
float* d_A;
|
||||
double *d_b, *d_x, *d_x_new;
|
||||
cudaStream_t stream1;
|
||||
cuda_safe_call(cudaStreamCreateWithFlags(&stream1, cudaStreamNonBlocking));
|
||||
cuda_safe_call(cudaMalloc(&d_b, sizeof(double) * N_ROWS));
|
||||
cuda_safe_call(cudaMalloc(&d_A, sizeof(float) * N_ROWS * N_ROWS));
|
||||
cuda_safe_call(cudaMalloc(&d_x, sizeof(double) * N_ROWS));
|
||||
cuda_safe_call(cudaMalloc(&d_x_new, sizeof(double) * N_ROWS));
|
||||
|
||||
cuda_safe_call(cudaMemsetAsync(d_x, 0, sizeof(double) * N_ROWS, stream1));
|
||||
cuda_safe_call(cudaMemsetAsync(d_x_new, 0, sizeof(double) * N_ROWS, stream1));
|
||||
cuda_safe_call(cudaMemcpyAsync(d_A, A, sizeof(float) * N_ROWS * N_ROWS, cudaMemcpyHostToDevice, stream1));
|
||||
cuda_safe_call(cudaMemcpyAsync(d_b, b, sizeof(double) * N_ROWS, cudaMemcpyHostToDevice, stream1));
|
||||
|
||||
// sdkCreateTimer(&timerGpu);
|
||||
// sdkStartTimer(&timerGpu);
|
||||
|
||||
double sumGPU = 0.0;
|
||||
if (gpumethod == 0)
|
||||
{
|
||||
sumGPU = JacobiMethodGpuCudaGraphExecKernelSetParams(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);
|
||||
}
|
||||
else if (gpumethod == 1)
|
||||
{
|
||||
sumGPU = JacobiMethodGpuCudaGraphExecUpdate(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);
|
||||
}
|
||||
else if (gpumethod == 2)
|
||||
{
|
||||
sumGPU = JacobiMethodGpu(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);
|
||||
}
|
||||
|
||||
// sdkStopTimer(&timerGpu);
|
||||
// printf("GPU Processing time: %f (ms)\n", sdkGetTimerValue(&timerGpu));
|
||||
|
||||
cuda_safe_call(cudaFree(d_b));
|
||||
cuda_safe_call(cudaFree(d_A));
|
||||
cuda_safe_call(cudaFree(d_x));
|
||||
cuda_safe_call(cudaFree(d_x_new));
|
||||
|
||||
cuda_safe_call(cudaFreeHost(A));
|
||||
cuda_safe_call(cudaFreeHost(b));
|
||||
|
||||
// printf("&&&& jacobiCudaGraphs %s\n", (fabs(sum - sumGPU) < conv_threshold) ? "PASSED" : "FAILED");
|
||||
|
||||
return (fabs(sum - sumGPU) < conv_threshold) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// Jacobi method on a linear system A*x = b,
|
||||
// where A is diagonally dominant and the exact solution consists
|
||||
// of all ones.
|
||||
|
||||
#include <cuda/experimental/__stf/internal/scalar_interface.cuh>
|
||||
#include <cuda/experimental/__stf/stream/stream_ctx.cuh>
|
||||
|
||||
#define N_ROWS 512
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
using namespace cuda::experimental::stf;
|
||||
|
||||
// 8 Rows of square-matrix A processed by each CTA.
|
||||
// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).
|
||||
#define ROWS_PER_CTA 8
|
||||
|
||||
// creates N_ROWS x N_ROWS matrix A with N_ROWS+1 on the diagonal and 1
|
||||
// elsewhere. The elements of the right hand side b all equal 2*n, hence the
|
||||
// exact solution x to A*x = b is a vector of ones.
|
||||
void createLinearSystem(float* A, double* b)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
b[i] = 2.0 * N_ROWS;
|
||||
for (j = 0; j < N_ROWS; j++)
|
||||
{
|
||||
A[i * N_ROWS + j] = 1.0;
|
||||
}
|
||||
A[i * N_ROWS + i] = N_ROWS + 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
static __global__ void
|
||||
JacobiMethod(const float* A, const double* b, const float conv_threshold, double* x, double* x_new, double* sum)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
__shared__ double x_shared[N_ROWS]; // N_ROWS == n
|
||||
__shared__ double b_shared[ROWS_PER_CTA + 1];
|
||||
|
||||
for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x)
|
||||
{
|
||||
x_shared[i] = x[i];
|
||||
}
|
||||
|
||||
if (threadIdx.x < ROWS_PER_CTA)
|
||||
{
|
||||
int k = threadIdx.x;
|
||||
for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);
|
||||
k += ROWS_PER_CTA, i += ROWS_PER_CTA)
|
||||
{
|
||||
b_shared[i % (ROWS_PER_CTA + 1)] = b[i];
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
|
||||
for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++)
|
||||
{
|
||||
double rowThreadSum = 0.0;
|
||||
for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x)
|
||||
{
|
||||
rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);
|
||||
}
|
||||
|
||||
for (int offset = tile32.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
rowThreadSum += tile32.shfl_down(rowThreadSum, offset);
|
||||
}
|
||||
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);
|
||||
}
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
if (threadIdx.x < ROWS_PER_CTA)
|
||||
{
|
||||
cg::thread_block_tile<ROWS_PER_CTA> tile8 = cg::tiled_partition<ROWS_PER_CTA>(cta);
|
||||
double temp_sum = 0.0;
|
||||
|
||||
int k = threadIdx.x;
|
||||
|
||||
for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);
|
||||
k += ROWS_PER_CTA, i += ROWS_PER_CTA)
|
||||
{
|
||||
double dx = b_shared[i % (ROWS_PER_CTA + 1)];
|
||||
dx /= A[i * N_ROWS + i];
|
||||
|
||||
x_new[i] = (x_shared[i] + dx);
|
||||
temp_sum += fabs(dx);
|
||||
}
|
||||
|
||||
for (int offset = tile8.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
temp_sum += tile8.shfl_down(temp_sum, offset);
|
||||
}
|
||||
|
||||
if (tile8.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(sum, temp_sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Thread block size for finalError kernel should be multiple of 32
|
||||
static __global__ void finalError(const double* x, double* g_sum)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
extern __shared__ double warpSum[];
|
||||
double sum = 0.0;
|
||||
|
||||
int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x)
|
||||
{
|
||||
double d = x[i] - 1.0;
|
||||
sum += fabs(d);
|
||||
}
|
||||
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
|
||||
for (int offset = tile32.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
sum += tile32.shfl_down(sum, offset);
|
||||
}
|
||||
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
warpSum[threadIdx.x / warpSize] = sum;
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
double blockSum = 0.0;
|
||||
if (threadIdx.x < (blockDim.x / warpSize))
|
||||
{
|
||||
blockSum = warpSum[threadIdx.x];
|
||||
}
|
||||
|
||||
if (threadIdx.x < 32)
|
||||
{
|
||||
for (int offset = tile32.size() / 2; offset > 0; offset /= 2)
|
||||
{
|
||||
blockSum += tile32.shfl_down(blockSum, offset);
|
||||
}
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(g_sum, blockSum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the Jacobi method for A*x = b on GPU without CUDA Graph.
|
||||
// double JacobiMethodGpu(const float *A, const double *b,
|
||||
// const float conv_threshold, const int max_iter,
|
||||
// double *x, double *x_new) {
|
||||
double JacobiMethodGpu(
|
||||
stream_ctx& ctx,
|
||||
logical_data<slice<float>>& A_handle,
|
||||
logical_data<slice<double>>& b_handle,
|
||||
const float conv_threshold,
|
||||
const int max_iter,
|
||||
logical_data<slice<double>>& x_handle,
|
||||
logical_data<slice<double>>& x_new_handle)
|
||||
{
|
||||
// CTA size
|
||||
dim3 nthreads(256, 1, 1);
|
||||
// grid size
|
||||
dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);
|
||||
|
||||
auto sum_handle = ctx.logical_data(shape_of<scalar_view<double>>()).set_symbol("sum");
|
||||
|
||||
int k;
|
||||
for (k = 0; k < max_iter; k++)
|
||||
{
|
||||
auto x_mode = (k & 1) == 0 ? access_mode::read : access_mode::rw;
|
||||
auto x_new_mode = (k & 1) == 0 ? access_mode::rw : access_mode::read;
|
||||
|
||||
ctx.task(A_handle.read(),
|
||||
b_handle.read(),
|
||||
task_dep<slice<double>>(x_handle, x_mode),
|
||||
task_dep<slice<double>>(x_new_handle, x_new_mode),
|
||||
sum_handle.write())
|
||||
.set_symbol("JacobiMethod")
|
||||
->*[&](cudaStream_t stream, auto A, auto b, auto x, auto x_new, auto d_sum) {
|
||||
cuda_try(cudaMemsetAsync(d_sum.addr, 0, sizeof(double), stream));
|
||||
|
||||
if ((k & 1) == 0)
|
||||
{
|
||||
JacobiMethod<<<nblocks, nthreads, 0, stream>>>(
|
||||
A.data_handle(), b.data_handle(), conv_threshold, x.data_handle(), x_new.data_handle(), d_sum.addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
JacobiMethod<<<nblocks, nthreads, 0, stream>>>(
|
||||
A.data_handle(), b.data_handle(), conv_threshold, x_new.data_handle(), x.data_handle(), d_sum.addr);
|
||||
}
|
||||
};
|
||||
|
||||
if (ctx.wait(sum_handle) <= conv_threshold)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto final_x_handle = ((k & 1) == 0) ? &x_new_handle : &x_handle;
|
||||
ctx.task(sum_handle.write(), final_x_handle->read()).set_symbol("finalError")
|
||||
->*[&](cudaStream_t stream, auto d_sum, auto final_x) {
|
||||
cuda_try(cudaMemsetAsync(d_sum.addr, 0, sizeof(double), stream));
|
||||
|
||||
nblocks.x = (N_ROWS / nthreads.x) + 1;
|
||||
size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);
|
||||
finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(final_x.data_handle(), d_sum.addr);
|
||||
};
|
||||
|
||||
return ctx.wait(sum_handle);
|
||||
}
|
||||
|
||||
// Run the Jacobi method for A*x = b on CPU.
|
||||
void JacobiMethodCPU(float* A, double* b, float conv_threshold, int max_iter, int* num_iter, double* x)
|
||||
{
|
||||
double* x_new = (double*) calloc(N_ROWS, sizeof(double));
|
||||
SCOPE(exit)
|
||||
{
|
||||
free(x_new);
|
||||
};
|
||||
|
||||
int k = 0;
|
||||
|
||||
for (; k < max_iter; k++)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
double temp_dx = b[i];
|
||||
for (int j = 0; j < N_ROWS; j++)
|
||||
{
|
||||
temp_dx -= A[i * N_ROWS + j] * x[j];
|
||||
}
|
||||
temp_dx /= A[i * N_ROWS + i];
|
||||
x_new[i] += temp_dx;
|
||||
sum += fabs(temp_dx);
|
||||
}
|
||||
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
x[i] = x_new[i];
|
||||
}
|
||||
|
||||
if (sum <= conv_threshold)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*num_iter = k + 1;
|
||||
}
|
||||
|
||||
template <typename Ctx>
|
||||
int run()
|
||||
{
|
||||
Ctx ctx;
|
||||
|
||||
double* b = cuda_try<cudaMallocHost<double>>(N_ROWS * sizeof(double), 0);
|
||||
SCOPE(exit)
|
||||
{
|
||||
cuda_try(cudaFreeHost(b));
|
||||
};
|
||||
|
||||
float* A = cuda_try<cudaMallocHost<float>>(N_ROWS * N_ROWS * sizeof(float), 0);
|
||||
SCOPE(exit)
|
||||
{
|
||||
cuda_try(cudaFreeHost(A));
|
||||
};
|
||||
|
||||
memset(b, 0, N_ROWS * sizeof(double));
|
||||
|
||||
memset(A, 0, N_ROWS * N_ROWS * sizeof(float));
|
||||
|
||||
createLinearSystem(A, b);
|
||||
// start with array of all zeroes
|
||||
double* x = (double*) calloc(N_ROWS, sizeof(double));
|
||||
SCOPE(exit)
|
||||
{
|
||||
free(x);
|
||||
};
|
||||
|
||||
auto A_handle = ctx.logical_data(A, N_ROWS * N_ROWS).set_symbol("A");
|
||||
auto b_handle = ctx.logical_data(b, N_ROWS).set_symbol("b");
|
||||
auto x_handle = ctx.logical_data(x, N_ROWS).set_symbol("x");
|
||||
auto x_new_handle = ctx.logical_data(shape_of<slice<double>>(N_ROWS)).set_symbol("x_new");
|
||||
|
||||
float conv_threshold = 1.0e-2;
|
||||
int max_iter = 4 * N_ROWS * N_ROWS;
|
||||
int cnt = 0;
|
||||
|
||||
JacobiMethodCPU(A, b, conv_threshold, max_iter, &cnt, x);
|
||||
|
||||
double sum = 0.0;
|
||||
// Compute error
|
||||
for (int i = 0; i < N_ROWS; i++)
|
||||
{
|
||||
double d = x[i] - 1.0;
|
||||
sum += fabs(d);
|
||||
}
|
||||
|
||||
ctx.task(x_handle.write()).set_symbol("memset x")->*[&](cudaStream_t stream, auto d_x) {
|
||||
cuda_try(cudaMemsetAsync(d_x.data_handle(), 0, sizeof(double) * N_ROWS, stream));
|
||||
};
|
||||
|
||||
ctx.task(x_new_handle.write()).set_symbol("memset x_new")->*[](cudaStream_t stream, auto d_x_new) {
|
||||
cuda_try(cudaMemsetAsync(d_x_new.data_handle(), 0, sizeof(double) * N_ROWS, stream));
|
||||
};
|
||||
|
||||
double sumGPU = JacobiMethodGpu(ctx, A_handle, b_handle, conv_threshold, max_iter, x_handle, x_new_handle);
|
||||
|
||||
ctx.finalize();
|
||||
|
||||
if (fabs(sum - sumGPU) > conv_threshold)
|
||||
{
|
||||
printf("&&&& jacobiCudaGraphs FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
return run<stream_ctx>();
|
||||
// run<graph_ctx>();
|
||||
}
|
||||
@@ -1,521 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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 sample implements a conjugate gradient solver on multiple GPU using
|
||||
* Unified Memory optimized prefetching and usage hints.
|
||||
*
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <cuda/experimental/__places/partitions/blocked_partition.cuh>
|
||||
#include <cuda/experimental/stf.cuh>
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
#include <cooperative_groups/reduce.h>
|
||||
|
||||
using namespace cuda::experimental::stf;
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
const char* sSDKname = "conjugateGradientMultiDeviceCG";
|
||||
|
||||
#define ENABLE_CPU_DEBUG_CODE 0
|
||||
#define THREADS_PER_BLOCK 64
|
||||
|
||||
__device__ double grid_dot_result = 0.0;
|
||||
|
||||
/* genTridiag: generate a random tridiagonal symmetric matrix */
|
||||
void genTridiag(slice<int> I, slice<int> J, slice<float> val, int N, int nz)
|
||||
{
|
||||
I(0) = 0, J(0) = 0, J(1) = 1;
|
||||
val(0) = (float) rand() / RAND_MAX + 10.0f;
|
||||
val(1) = (float) rand() / RAND_MAX;
|
||||
int start;
|
||||
|
||||
for (int i = 1; i < N; i++)
|
||||
{
|
||||
if (i > 1)
|
||||
{
|
||||
I(i) = I(i - 1) + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
I(1) = 2;
|
||||
}
|
||||
|
||||
start = (i - 1) * 3 + 2;
|
||||
J(start) = i - 1;
|
||||
J(start + 1) = i;
|
||||
|
||||
if (i < N - 1)
|
||||
{
|
||||
J(start + 2) = i + 1;
|
||||
}
|
||||
|
||||
val(start) = val(start - 1);
|
||||
val(start + 1) = (float) rand() / RAND_MAX + 10.0f;
|
||||
|
||||
if (i < N - 1)
|
||||
{
|
||||
val(start + 2) = (float) rand() / RAND_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
I(N) = nz;
|
||||
}
|
||||
|
||||
// I - contains location of the given non-zero element in the row of the matrix
|
||||
// J - contains location of the given non-zero element in the column of the
|
||||
// matrix val - contains values of the given non-zero elements of the matrix
|
||||
// inputVecX - input vector to be multiplied
|
||||
// outputVecY - resultant vector
|
||||
void cpuSpMV(int* I, int* J, float* val, int /*unused*/, int num_rows, float alpha, float* inputVecX, float* outputVecY)
|
||||
{
|
||||
for (int i = 0; i < num_rows; i++)
|
||||
{
|
||||
int num_elems_this_row = I[i + 1] - I[i];
|
||||
|
||||
float output = 0.0;
|
||||
for (int j = 0; j < num_elems_this_row; j++)
|
||||
{
|
||||
output += alpha * val[I[i] + j] * inputVecX[J[I[i] + j]];
|
||||
}
|
||||
outputVecY[i] = output;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float dotProduct(float* vecA, float* vecB, int size)
|
||||
{
|
||||
float result = 0.0;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
result = result + (vecA[i] * vecB[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void scaleVector(float* vec, float alpha, int size)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
vec[i] = alpha * vec[i];
|
||||
}
|
||||
}
|
||||
|
||||
void saxpy(float* x, float* y, float a, int size)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
y[i] = a * x[i] + y[i];
|
||||
}
|
||||
}
|
||||
|
||||
void cpuConjugateGrad(int* I, int* J, float* val, float* x, float* Ax, float* p, float* r, int nnz, int N, float tol)
|
||||
{
|
||||
int max_iter = 10000;
|
||||
|
||||
float alpha = 1.0;
|
||||
float alpham1 = -1.0;
|
||||
float r0 = 0.0, b, a, na;
|
||||
|
||||
cpuSpMV(I, J, val, nnz, N, alpha, x, Ax);
|
||||
saxpy(Ax, r, alpham1, N);
|
||||
|
||||
float r1 = dotProduct(r, r, N);
|
||||
|
||||
int k = 1;
|
||||
|
||||
while (r1 > tol * tol && k <= max_iter)
|
||||
{
|
||||
if (k > 1)
|
||||
{
|
||||
b = r1 / r0;
|
||||
scaleVector(p, b, N);
|
||||
|
||||
saxpy(r, p, alpha, N);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
p[i] = r[i];
|
||||
}
|
||||
}
|
||||
|
||||
cpuSpMV(I, J, val, nnz, N, alpha, p, Ax);
|
||||
|
||||
float dot = dotProduct(p, Ax, N);
|
||||
a = r1 / dot;
|
||||
|
||||
saxpy(p, x, a, N);
|
||||
na = -a;
|
||||
saxpy(Ax, r, na, N);
|
||||
|
||||
r0 = r1;
|
||||
r1 = dotProduct(r, r, N);
|
||||
|
||||
printf("\nCPU code iteration = %3d, residual = %e\n", k, sqrt(r1));
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename thread_hierarchy_t>
|
||||
__device__ void gpuSpMV(
|
||||
slice<const int> I,
|
||||
slice<const int> J,
|
||||
slice<const float> val,
|
||||
int nnz,
|
||||
int num_rows,
|
||||
float alpha,
|
||||
slice<float> inputVecX,
|
||||
slice<float> outputVecY,
|
||||
const thread_hierarchy_t& t)
|
||||
{
|
||||
for (int i = t.rank(); i < num_rows; i += t.size())
|
||||
{
|
||||
int row_elem = I(i);
|
||||
int next_row_elem = I(i + 1);
|
||||
int num_elems_this_row = next_row_elem - row_elem;
|
||||
|
||||
float output = 0.0;
|
||||
for (int j = 0; j < num_elems_this_row; j++)
|
||||
{
|
||||
output += alpha * val(row_elem + j) * inputVecX(J(row_elem + j));
|
||||
}
|
||||
|
||||
outputVecY(i) = output;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename thread_hierarchy_t>
|
||||
__device__ void gpuSaxpy(slice<float> x, slice<float> y, float a, int size, const thread_hierarchy_t& t)
|
||||
{
|
||||
for (int i = t.rank(); i < size; i += t.size())
|
||||
{
|
||||
y(i) = a * x(i) + y(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename thread_hierarchy_t>
|
||||
__device__ double
|
||||
gpuDotProduct(slice<float> vecA, slice<float> vecB, int size, double* dot_result, thread_hierarchy_t& t)
|
||||
{
|
||||
slice<double> tmp = t.template storage<double>(1);
|
||||
|
||||
cg::thread_block cta = cooperative_groups::this_thread_block();
|
||||
|
||||
double temp_sum = 0.0;
|
||||
|
||||
for (int i = t.rank(); i < size; i += t.size())
|
||||
{
|
||||
temp_sum += (double) (vecA(i) * vecB(i));
|
||||
}
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());
|
||||
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
tmp[tile32.meta_group_rank()] = temp_sum;
|
||||
}
|
||||
|
||||
cta.sync();
|
||||
|
||||
if (tile32.meta_group_rank() == 0)
|
||||
{
|
||||
temp_sum = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;
|
||||
temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());
|
||||
|
||||
if (tile32.thread_rank() == 0)
|
||||
{
|
||||
atomicAdd(&grid_dot_result, temp_sum);
|
||||
}
|
||||
}
|
||||
|
||||
t.sync();
|
||||
|
||||
if (t.rank(0, -1) == 0)
|
||||
{
|
||||
atomicAdd_system(dot_result, grid_dot_result);
|
||||
grid_dot_result = 0.0;
|
||||
}
|
||||
|
||||
t.sync();
|
||||
return *dot_result;
|
||||
}
|
||||
|
||||
template <typename thread_hierarchy_t>
|
||||
__device__ void gpuCopyVector(slice<float> srcA, slice<float> destB, int size, const thread_hierarchy_t& t)
|
||||
{
|
||||
for (int i = t.rank(); i < size; i += t.size())
|
||||
{
|
||||
destB(i) = srcA(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename thread_hierarchy_t>
|
||||
__device__ void
|
||||
gpuScaleVectorAndSaxpy(slice<float> x, slice<float> y, float a, float scale, int size, const thread_hierarchy_t& t)
|
||||
{
|
||||
for (int i = t.rank(); i < size; i += t.size())
|
||||
{
|
||||
y(i) = a * x(i) + scale * y(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename thread_hierarchy_t>
|
||||
__device__ void multiGpuConjugateGradient(
|
||||
thread_hierarchy_t t,
|
||||
slice<const int> I,
|
||||
slice<const int> J,
|
||||
slice<const float> val,
|
||||
slice<float> x,
|
||||
slice<float> Ax,
|
||||
slice<float> p,
|
||||
slice<float> r,
|
||||
double* dot_result,
|
||||
int nnz,
|
||||
int N,
|
||||
float tol)
|
||||
{
|
||||
const int max_iter = 10000;
|
||||
|
||||
float alpha = 1.0;
|
||||
float alpham1 = -1.0;
|
||||
float r0 = 0.0, r1, b, a, na;
|
||||
|
||||
for (int i = t.rank(); i < N; i += t.size())
|
||||
{
|
||||
r[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
|
||||
gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, t);
|
||||
|
||||
gpuSaxpy(Ax, r, alpham1, N, t);
|
||||
|
||||
r1 = gpuDotProduct(r, r, N, dot_result, t);
|
||||
|
||||
int k = 1;
|
||||
while (r1 > tol * tol && k <= max_iter)
|
||||
{
|
||||
if (k > 1)
|
||||
{
|
||||
b = r1 / r0;
|
||||
gpuScaleVectorAndSaxpy(r, p, alpha, b, N, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpuCopyVector(r, p, N, t);
|
||||
}
|
||||
|
||||
gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, t);
|
||||
|
||||
if (t.rank() == 0)
|
||||
{
|
||||
*dot_result = 0.0;
|
||||
}
|
||||
|
||||
a = r1 / gpuDotProduct(p, Ax, N, dot_result, t);
|
||||
|
||||
gpuSaxpy(p, x, a, N, t);
|
||||
|
||||
na = -a;
|
||||
|
||||
gpuSaxpy(Ax, r, na, N, t);
|
||||
|
||||
r0 = r1;
|
||||
|
||||
if (t.rank() == 0)
|
||||
{
|
||||
*dot_result = 0.0;
|
||||
}
|
||||
|
||||
r1 = gpuDotProduct(r, r, N, dot_result, t);
|
||||
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
stream_ctx ctx;
|
||||
#if 0
|
||||
constexpr size_t kNumGpusRequired = 8;
|
||||
#else
|
||||
constexpr size_t kNumGpusRequired = 1;
|
||||
#endif
|
||||
int N = 0, nz = 0, *I = NULL, *J = NULL;
|
||||
float* val = NULL;
|
||||
const float tol = 1e-5f;
|
||||
float* x;
|
||||
float rhs = 1.0;
|
||||
float r1;
|
||||
float *r, *p, *Ax;
|
||||
|
||||
// printf("Starting [%s]...\n", sSDKname);
|
||||
|
||||
/* Generate a random tridiagonal symmetric matrix in CSR format */
|
||||
N = 10485760 * 2;
|
||||
nz = (N - 2) * 3 + 4;
|
||||
|
||||
I = (int*) malloc(sizeof(int) * (N + 1));
|
||||
J = (int*) malloc(sizeof(int) * nz);
|
||||
val = (float*) malloc(sizeof(float) * nz);
|
||||
float* val_cpu = (float*) malloc(sizeof(float) * nz);
|
||||
|
||||
auto handle_I = ctx.logical_data(I, {(unsigned) (N + 1)});
|
||||
auto handle_J = ctx.logical_data(J, {(unsigned) nz});
|
||||
auto handle_val = ctx.logical_data(val, {(unsigned) nz});
|
||||
|
||||
ctx.host_launch(handle_I.write(), handle_J.write(), handle_val.write())->*[=](auto I, auto J, auto val) {
|
||||
genTridiag(I, J, val, N, nz);
|
||||
memcpy(val_cpu, val.data_handle(), sizeof(float) * nz);
|
||||
};
|
||||
|
||||
double* dot_result = (double*) malloc(sizeof(double));
|
||||
dot_result[0] = 0.0;
|
||||
|
||||
x = (float*) malloc(sizeof(float) * N);
|
||||
r = (float*) malloc(sizeof(float) * N);
|
||||
p = (float*) malloc(sizeof(float) * N);
|
||||
Ax = (float*) malloc(sizeof(float) * N);
|
||||
|
||||
auto handle_r = ctx.logical_data(r, {(unsigned) N});
|
||||
auto handle_p = ctx.logical_data(p, {(unsigned) N});
|
||||
auto handle_Ax = ctx.logical_data(Ax, {(unsigned) N});
|
||||
auto handle_x = ctx.logical_data(x, {(unsigned) N});
|
||||
auto handle_dot_result = ctx.logical_data(dot_result, {(unsigned) 1});
|
||||
|
||||
// std::cout << "\nRunning on GPUs = " << kNumGpusRequired << '\n';
|
||||
const int sMemSize = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);
|
||||
|
||||
// auto all_devs = exec_place::repeat<blocked_partition>(exec_place::device(0), kNumGpusRequired);
|
||||
auto all_devs = exec_place::n_devices(kNumGpusRequired);
|
||||
|
||||
/* The grid size is 0 and will be computed upon launch */
|
||||
auto spec = con(con(THREADS_PER_BLOCK, mem(sMemSize)));
|
||||
ctx.launch(
|
||||
spec,
|
||||
all_devs,
|
||||
handle_I.read(),
|
||||
handle_J.read(),
|
||||
handle_val.read(),
|
||||
handle_x.write(),
|
||||
handle_Ax.write(),
|
||||
handle_p.write(),
|
||||
handle_r.write(),
|
||||
handle_dot_result.write())
|
||||
->*[=]
|
||||
_CCCL_DEVICE(auto t,
|
||||
slice<const int> I,
|
||||
slice<const int> J,
|
||||
slice<const float> val,
|
||||
slice<float> x,
|
||||
slice<float> Ax,
|
||||
slice<float> p,
|
||||
slice<float> r,
|
||||
slice<double> dot_result) {
|
||||
multiGpuConjugateGradient(t, I, J, val, x, Ax, p, r, dot_result.data_handle(), nz, N, tol);
|
||||
};
|
||||
|
||||
ctx.finalize();
|
||||
|
||||
r1 = dot_result[0];
|
||||
|
||||
printf("GPU Final, residual = %e \n ", sqrt(r1));
|
||||
|
||||
#if ENABLE_CPU_DEBUG_CODE
|
||||
float* Ax_cpu = (float*) malloc(sizeof(float) * N);
|
||||
float* r_cpu = (float*) malloc(sizeof(float) * N);
|
||||
float* p_cpu = (float*) malloc(sizeof(float) * N);
|
||||
float* x_cpu = (float*) malloc(sizeof(float) * N);
|
||||
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
r_cpu[i] = 1.0;
|
||||
Ax_cpu[i] = x_cpu[i] = 0.0;
|
||||
}
|
||||
cpuConjugateGrad(I, J, val, x_cpu, Ax_cpu, p_cpu, r_cpu, nz, N, tol);
|
||||
#endif
|
||||
|
||||
float rsum, diff, err = 0.0;
|
||||
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
rsum = 0.0;
|
||||
|
||||
for (int j = I[i]; j < I[i + 1]; j++)
|
||||
{
|
||||
rsum += val_cpu[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs);
|
||||
|
||||
if (diff > err)
|
||||
{
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_CPU_DEBUG_CODE
|
||||
free(Ax_cpu);
|
||||
free(r_cpu);
|
||||
free(p_cpu);
|
||||
free(x_cpu);
|
||||
#endif
|
||||
|
||||
printf("Test Summary: Error amount = %f \n", err);
|
||||
fprintf(stdout, "&&&& conjugateGradientMultiDeviceCG %s\n", (sqrt(r1) < tol) ? "PASSED" : "FAILED");
|
||||
exit((sqrt(r1) < tol) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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 sample evaluates fair call price for a
|
||||
* given set of European options using Monte Carlo approach.
|
||||
* See supplied whitepaper for more explanations.
|
||||
*/
|
||||
|
||||
#include "MonteCarlo_gold.cu"
|
||||
#include "MonteCarlo_kernel.cu"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Common functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
float randFloat(float low, float high)
|
||||
{
|
||||
float t = (float) rand() / (float) RAND_MAX;
|
||||
return (1.0f - t) * low + t * high;
|
||||
}
|
||||
|
||||
/// Utility function to tweak problem size for small GPUs
|
||||
int adjustProblemSize(int GPU_N, int default_nOptions)
|
||||
{
|
||||
int nOptions = default_nOptions;
|
||||
|
||||
for (int i = 0; i < GPU_N; i++)
|
||||
{
|
||||
cudaDeviceProp deviceProp;
|
||||
cuda_safe_call(cudaGetDeviceProperties(&deviceProp, i));
|
||||
int cudaCores = 80;
|
||||
|
||||
if (cudaCores <= 32)
|
||||
{
|
||||
nOptions = (nOptions < cudaCores / 2 ? nOptions : cudaCores / 2);
|
||||
}
|
||||
}
|
||||
|
||||
return nOptions;
|
||||
}
|
||||
|
||||
int adjustGridSize(int GPUIndex, int defaultGridSize)
|
||||
{
|
||||
cudaDeviceProp deviceProp;
|
||||
cuda_safe_call(cudaGetDeviceProperties(&deviceProp, GPUIndex));
|
||||
int maxGridSize = deviceProp.multiProcessorCount * 40;
|
||||
return ((defaultGridSize > maxGridSize) ? maxGridSize : defaultGridSize);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CPU reference functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
extern "C" void MonteCarloCPU(TOptionValue& callValue, TOptionData optionData, float* h_Random, int pathN);
|
||||
|
||||
// Black-Scholes formula for call options
|
||||
extern "C" void BlackScholesCall(float& CallResult, TOptionData optionData);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Single-threaded multi-GPU solver using STF
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void multiSolver(TOptionPlan* plan, int nPlans)
|
||||
{
|
||||
stream_ctx ctx;
|
||||
|
||||
for (int i = 0; i < nPlans; i++)
|
||||
{
|
||||
cuda_safe_call(cudaSetDevice(plan[i].device));
|
||||
|
||||
initMonteCarloGPU(ctx, &plan[i]);
|
||||
MonteCarloGPU(ctx, &plan[i]);
|
||||
closeMonteCarloGPU(ctx, &plan[i]);
|
||||
}
|
||||
|
||||
ctx.finalize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int GPU_N;
|
||||
cuda_safe_call(cudaGetDeviceCount(&GPU_N));
|
||||
int nOptions = 8 * 1024;
|
||||
|
||||
nOptions = adjustProblemSize(GPU_N, nOptions);
|
||||
|
||||
int OPT_N = nOptions * GPU_N;
|
||||
int PATH_N = 262144;
|
||||
|
||||
// Input data array
|
||||
TOptionData* optionData = new TOptionData[OPT_N];
|
||||
// Final GPU MC results
|
||||
TOptionValue* callValueGPU = new TOptionValue[OPT_N];
|
||||
//"Theoretical" call values by Black-Scholes formula
|
||||
float* callValueBS = new float[OPT_N];
|
||||
// Solver config
|
||||
TOptionPlan* optionSolver = new TOptionPlan[GPU_N];
|
||||
|
||||
int i;
|
||||
double delta, ref, sumDelta, sumRef, sumReserve;
|
||||
|
||||
srand(123);
|
||||
|
||||
for (i = 0; i < OPT_N; i++)
|
||||
{
|
||||
optionData[i].S = randFloat(5.0f, 50.0f);
|
||||
optionData[i].X = randFloat(10.0f, 25.0f);
|
||||
optionData[i].T = randFloat(1.0f, 5.0f);
|
||||
optionData[i].R = 0.06f;
|
||||
optionData[i].V = 0.10f;
|
||||
callValueGPU[i].Expected = -1.0f;
|
||||
callValueGPU[i].Confidence = -1.0f;
|
||||
}
|
||||
|
||||
// Get option count for each GPU
|
||||
for (i = 0; i < GPU_N; i++)
|
||||
{
|
||||
optionSolver[i].optionCount = OPT_N / GPU_N;
|
||||
}
|
||||
|
||||
// Take into account cases with "odd" option counts
|
||||
for (i = 0; i < (OPT_N % GPU_N); i++)
|
||||
{
|
||||
optionSolver[i].optionCount++;
|
||||
}
|
||||
|
||||
// Assign GPU option ranges
|
||||
int gpuBase = 0;
|
||||
|
||||
for (i = 0; i < GPU_N; i++)
|
||||
{
|
||||
optionSolver[i].device = i;
|
||||
optionSolver[i].optionData = optionData + gpuBase;
|
||||
optionSolver[i].callValue = callValueGPU + gpuBase;
|
||||
optionSolver[i].pathN = PATH_N;
|
||||
optionSolver[i].gridSize = adjustGridSize(optionSolver[i].device, optionSolver[i].optionCount);
|
||||
gpuBase += optionSolver[i].optionCount;
|
||||
}
|
||||
|
||||
multiSolver(optionSolver, GPU_N);
|
||||
|
||||
// Compare Monte Carlo and Black-Scholes results
|
||||
sumDelta = 0;
|
||||
sumRef = 0;
|
||||
sumReserve = 0;
|
||||
|
||||
for (i = 0; i < OPT_N; i++)
|
||||
{
|
||||
BlackScholesCall(callValueBS[i], optionData[i]);
|
||||
delta = fabs(callValueBS[i] - callValueGPU[i].Expected);
|
||||
ref = callValueBS[i];
|
||||
sumDelta += delta;
|
||||
sumRef += fabs(ref);
|
||||
|
||||
if (delta > 1e-6)
|
||||
{
|
||||
sumReserve += callValueGPU[i].Confidence / delta;
|
||||
}
|
||||
}
|
||||
|
||||
sumReserve /= OPT_N;
|
||||
|
||||
delete[] optionSolver;
|
||||
delete[] callValueBS;
|
||||
delete[] callValueGPU;
|
||||
delete[] optionData;
|
||||
|
||||
if (sumReserve <= 1.0f)
|
||||
{
|
||||
printf("Test failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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 MONTECARLO_COMMON_H
|
||||
#define MONTECARLO_COMMON_H
|
||||
#include <cuda/experimental/stf.cuh>
|
||||
|
||||
#include "curand_kernel.h"
|
||||
#include "realtype.cuh"
|
||||
|
||||
using namespace cuda::experimental::stf;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct
|
||||
{
|
||||
float S;
|
||||
float X;
|
||||
float T;
|
||||
float R;
|
||||
float V;
|
||||
} TOptionData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float Expected;
|
||||
float Confidence;
|
||||
} TOptionValue;
|
||||
|
||||
// Preprocessed input option data
|
||||
typedef struct
|
||||
{
|
||||
real S;
|
||||
real X;
|
||||
real MuByT;
|
||||
real VBySqrtT;
|
||||
} __TOptionData;
|
||||
|
||||
// GPU outputs before CPU postprocessing
|
||||
typedef struct
|
||||
{
|
||||
real Expected;
|
||||
real Confidence;
|
||||
} __TOptionValue;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Device ID for multi-GPU version
|
||||
int device;
|
||||
// Option count for this plan
|
||||
int optionCount;
|
||||
|
||||
// Host-side data source and result destination
|
||||
TOptionData* optionData;
|
||||
TOptionValue* callValue;
|
||||
logical_data<slice<__TOptionData>> preproc_optionData_handle;
|
||||
logical_data<slice<__TOptionValue>> callValue_handle;
|
||||
|
||||
// Temporary Host-side pinned memory for async + faster data transfers
|
||||
__TOptionValue* h_CallValue;
|
||||
|
||||
// Host-side option data
|
||||
void* h_OptionData;
|
||||
|
||||
// Random number generator states
|
||||
logical_data<slice<curandState>> rngStates_handle;
|
||||
|
||||
// Pseudorandom samples count
|
||||
int pathN;
|
||||
|
||||
int gridSize;
|
||||
} TOptionPlan;
|
||||
|
||||
#endif
|
||||
@@ -1,160 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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 "MonteCarlo_common.cuh"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Black-Scholes formula for Monte Carlo results validation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define A1 0.31938153
|
||||
#define A2 -0.356563782
|
||||
#define A3 1.781477937
|
||||
#define A4 -1.821255978
|
||||
#define A5 1.330274429
|
||||
#define RSQRT2PI 0.39894228040143267793994605993438
|
||||
|
||||
// Polynomial approximation of
|
||||
// cumulative normal distribution function
|
||||
double CND(double d)
|
||||
{
|
||||
double K = 1.0 / (1.0 + 0.2316419 * fabs(d));
|
||||
|
||||
double cnd = RSQRT2PI * exp(-0.5 * d * d) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
|
||||
|
||||
if (d > 0)
|
||||
{
|
||||
cnd = 1.0 - cnd;
|
||||
}
|
||||
|
||||
return cnd;
|
||||
}
|
||||
|
||||
// Black-Scholes formula for call value
|
||||
extern "C" void BlackScholesCall(float& callValue, TOptionData optionData)
|
||||
{
|
||||
double S = optionData.S;
|
||||
double X = optionData.X;
|
||||
double T = optionData.T;
|
||||
double R = optionData.R;
|
||||
double V = optionData.V;
|
||||
|
||||
double sqrtT = sqrt(T);
|
||||
double d1 = (log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT);
|
||||
double d2 = d1 - V * sqrtT;
|
||||
double CNDD1 = CND(d1);
|
||||
double CNDD2 = CND(d2);
|
||||
double expRT = exp(-R * T);
|
||||
|
||||
callValue = (float) (S * CNDD1 - X * expRT * CNDD2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// CPU Monte Carlo
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static double CPU_endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)
|
||||
{
|
||||
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0) ? callValue : 0;
|
||||
}
|
||||
|
||||
#define CURAND_SAFE_CALL(call) \
|
||||
do \
|
||||
{ \
|
||||
const curandStatus_t err = (call); \
|
||||
if (CURAND_STATUS_SUCCESS != err) \
|
||||
{ \
|
||||
int dev = -1; \
|
||||
cudaGetDevice(&dev); \
|
||||
fprintf(stderr, "%s:%u [device %d] CURAND error in call %s.\n", __FILE__, __LINE__, dev, #call); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
extern "C" void MonteCarloCPU(TOptionValue& callValue, TOptionData optionData, float* h_Samples, int pathN)
|
||||
{
|
||||
const double S = optionData.S;
|
||||
const double X = optionData.X;
|
||||
const double T = optionData.T;
|
||||
const double R = optionData.R;
|
||||
const double V = optionData.V;
|
||||
const double MuByT = (R - 0.5 * V * V) * T;
|
||||
const double VBySqrtT = V * sqrt(T);
|
||||
|
||||
float* samples;
|
||||
curandGenerator_t gen;
|
||||
|
||||
CURAND_SAFE_CALL(curandCreateGeneratorHost(&gen, CURAND_RNG_PSEUDO_DEFAULT));
|
||||
unsigned long long seed = 1234ULL;
|
||||
CURAND_SAFE_CALL(curandSetPseudoRandomGeneratorSeed(gen, seed));
|
||||
|
||||
if (h_Samples != NULL)
|
||||
{
|
||||
samples = h_Samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
samples = (float*) malloc(pathN * sizeof(float));
|
||||
CURAND_SAFE_CALL(curandGenerateNormal(gen, samples, pathN, 0.0, 1.0));
|
||||
}
|
||||
|
||||
// for(int i=0; i<10; i++) printf("CPU sample = %f\n", samples[i]);
|
||||
|
||||
double sum = 0, sum2 = 0;
|
||||
|
||||
for (int pos = 0; pos < pathN; pos++)
|
||||
{
|
||||
double sample = samples[pos];
|
||||
double callValue = CPU_endCallValue(S, X, sample, MuByT, VBySqrtT);
|
||||
sum += callValue;
|
||||
sum2 += callValue * callValue;
|
||||
}
|
||||
|
||||
if (h_Samples == NULL)
|
||||
{
|
||||
free(samples);
|
||||
}
|
||||
|
||||
CURAND_SAFE_CALL(curandDestroyGenerator(gen));
|
||||
|
||||
// Derive average from the total sum and discount by riskfree rate
|
||||
callValue.Expected = (float) (exp(-R * T) * sum / (double) pathN);
|
||||
// Standard deviation
|
||||
double stdDev = sqrt(((double) pathN * sum2 - sum * sum) / ((double) pathN * (double) (pathN - 1)));
|
||||
// Confidence width; in 95% of all cases theoretical value lies within these
|
||||
// borders
|
||||
callValue.Confidence = (float) (exp(-R * T) * 1.96 * stdDev / sqrt((double) pathN));
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "MonteCarlo_reduction.cuh"
|
||||
|
||||
// This will output the proper error string when calling cudaGetLastError
|
||||
#define getLastCudaError(msg) __getLastCudaError((msg), __FILE__, __LINE__)
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
inline void __getLastCudaError(const char* errorMessage, const char* file, const int line)
|
||||
{
|
||||
cudaError_t err = cudaGetLastError();
|
||||
|
||||
if (cudaSuccess != err)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"%s(%i) : getLastCudaError() CUDA error:"
|
||||
" %s : (%d) %s.\n",
|
||||
file,
|
||||
line,
|
||||
errorMessage,
|
||||
static_cast<int>(err),
|
||||
cudaGetErrorString(err));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Internal GPU-side data structures
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define MAX_OPTIONS (1024 * 1024)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Overloaded shortcut payoff functions for different precision modes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)
|
||||
{
|
||||
float callValue = S * __expf(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0.0F) ? callValue : 0.0F;
|
||||
}
|
||||
|
||||
__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)
|
||||
{
|
||||
double callValue = S * exp(MuByT + VBySqrtT * r) - X;
|
||||
return (callValue > 0.0) ? callValue : 0.0;
|
||||
}
|
||||
|
||||
#define THREAD_N 256
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This kernel computes the integral over all paths using a single thread block
|
||||
// per option. It is fastest when the number of thread blocks times the work per
|
||||
// block is high enough to keep the GPU busy.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static __global__ void MonteCarloOneBlockPerOption(
|
||||
curandState* __restrict rngStates,
|
||||
const __TOptionData* __restrict d_OptionData,
|
||||
__TOptionValue* __restrict d_CallValue,
|
||||
int pathN,
|
||||
int optionN)
|
||||
{
|
||||
// Handle to thread block group
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);
|
||||
|
||||
const int SUM_N = THREAD_N;
|
||||
__shared__ real s_SumCall[SUM_N];
|
||||
__shared__ real s_Sum2Call[SUM_N];
|
||||
|
||||
// determine global thread id
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
// Copy random number state to local memory for efficiency
|
||||
curandState localState = rngStates[tid];
|
||||
for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x)
|
||||
{
|
||||
const real S = d_OptionData[optionIndex].S;
|
||||
const real X = d_OptionData[optionIndex].X;
|
||||
const real MuByT = d_OptionData[optionIndex].MuByT;
|
||||
const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;
|
||||
|
||||
// Cycle through the entire samples array:
|
||||
// derive end stock price for each path
|
||||
// accumulate partial integrals into intermediate shared memory buffer
|
||||
for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x)
|
||||
{
|
||||
__TOptionValue sumCall = {0, 0};
|
||||
|
||||
_CCCL_PRAGMA_UNROLL(8)
|
||||
for (int i = iSum; i < pathN; i += SUM_N)
|
||||
{
|
||||
real r = curand_normal(&localState);
|
||||
real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);
|
||||
sumCall.Expected += callValue;
|
||||
sumCall.Confidence += callValue * callValue;
|
||||
}
|
||||
|
||||
s_SumCall[iSum] = sumCall.Expected;
|
||||
s_Sum2Call[iSum] = sumCall.Confidence;
|
||||
}
|
||||
|
||||
// Reduce shared memory accumulators
|
||||
// and write final result to global memory
|
||||
cg::sync(cta);
|
||||
sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
static __global__ void rngSetupStates(curandState* rngState, int device_id)
|
||||
{
|
||||
// determine global thread id
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
// Each threadblock gets different seed,
|
||||
// Threads within a threadblock get different sequence numbers
|
||||
curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Host-side interface to GPU Monte Carlo
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Ctx>
|
||||
void initMonteCarloGPU(Ctx& ctx, TOptionPlan* plan)
|
||||
{
|
||||
plan->h_OptionData = new __TOptionData[plan->optionCount];
|
||||
plan->h_CallValue = new __TOptionValue[plan->optionCount];
|
||||
cuda_safe_call(
|
||||
cudaHostRegister(plan->h_OptionData, plan->optionCount * sizeof(__TOptionData), cudaHostRegisterPortable));
|
||||
cuda_safe_call(
|
||||
cudaHostRegister(plan->h_CallValue, plan->optionCount * sizeof(__TOptionValue), cudaHostRegisterPortable));
|
||||
|
||||
// Register this vector
|
||||
plan->preproc_optionData_handle =
|
||||
ctx.logical_data((__TOptionData*) plan->h_OptionData, plan->optionCount).set_symbol("preproc_optionData");
|
||||
plan->callValue_handle =
|
||||
ctx.logical_data((__TOptionValue*) plan->h_CallValue, plan->optionCount).set_symbol("callValue");
|
||||
plan->rngStates_handle =
|
||||
ctx.logical_data(shape_of<slice<curandState>>(plan->gridSize * THREAD_N)).set_symbol("rngStates");
|
||||
|
||||
cuda_safe_call(cudaSetDevice(plan->device));
|
||||
|
||||
// Allocate states for pseudo random number generators
|
||||
ctx.task(plan->rngStates_handle.write()).set_symbol("rngSetupStates")->*[&](cudaStream_t stream, auto rngStates) {
|
||||
cuda_safe_call(
|
||||
cudaMemsetAsync(rngStates.data_handle(), 0, plan->gridSize * THREAD_N * sizeof(curandState), stream));
|
||||
getLastCudaError("cudaMemsetAsync failed.\n");
|
||||
|
||||
rngSetupStates<<<plan->gridSize, THREAD_N, 0, stream>>>(rngStates.data_handle(), plan->device);
|
||||
getLastCudaError("rngSetupStates kernel failed.\n");
|
||||
};
|
||||
}
|
||||
|
||||
// Compute statistics and deallocate internal device memory
|
||||
template <typename Ctx>
|
||||
void closeMonteCarloGPU(Ctx& ctx, TOptionPlan* plan)
|
||||
{
|
||||
ctx.host_launch(plan->callValue_handle.rw()).set_symbol("compute_stats")->*[&](auto h_CallValue) {
|
||||
for (int i = 0; i < plan->optionCount; i++)
|
||||
{
|
||||
const double RT = plan->optionData[i].R * plan->optionData[i].T;
|
||||
const double sum = h_CallValue.data_handle()[i].Expected;
|
||||
const double sum2 = h_CallValue.data_handle()[i].Confidence;
|
||||
const double pathN = plan->pathN;
|
||||
plan->callValue[i].Expected = (float) (exp(-RT) * sum / pathN);
|
||||
double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));
|
||||
plan->callValue[i].Confidence = (float) (exp(-RT) * 1.96 * stdDev / sqrt(pathN));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Main computations
|
||||
template <typename Ctx>
|
||||
void MonteCarloGPU(Ctx& ctx, TOptionPlan* plan)
|
||||
{
|
||||
if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS)
|
||||
{
|
||||
printf("MonteCarloGPU(): bad option count.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Preprocess computations on the host
|
||||
ctx.host_launch(plan->preproc_optionData_handle.rw()).set_symbol("preprocess")->*[&](auto h_preproc_OptionData) {
|
||||
for (int i = 0; i < plan->optionCount; i++)
|
||||
{
|
||||
const double T = plan->optionData[i].T;
|
||||
const double R = plan->optionData[i].R;
|
||||
const double V = plan->optionData[i].V;
|
||||
const double MuByT = (R - 0.5 * V * V) * T;
|
||||
const double VBySqrtT = V * sqrt(T);
|
||||
h_preproc_OptionData.data_handle()[i].S = (real) plan->optionData[i].S;
|
||||
h_preproc_OptionData.data_handle()[i].X = (real) plan->optionData[i].X;
|
||||
h_preproc_OptionData.data_handle()[i].MuByT = (real) MuByT;
|
||||
h_preproc_OptionData.data_handle()[i].VBySqrtT = (real) VBySqrtT;
|
||||
}
|
||||
};
|
||||
|
||||
ctx.task(plan->preproc_optionData_handle.read(), plan->callValue_handle.write(), plan->rngStates_handle.rw())
|
||||
.set_symbol("MonteCarloOneBlockPerOption")
|
||||
->*[&](cudaStream_t stream, auto preproc_optionData, auto callValue_handle, auto rngStates) {
|
||||
MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(
|
||||
rngStates.data_handle(),
|
||||
preproc_optionData.data_handle(),
|
||||
callValue_handle.data_handle(),
|
||||
plan->pathN,
|
||||
plan->optionCount);
|
||||
getLastCudaError("MonteCarloOneBlockPerOption() execution failed\n");
|
||||
};
|
||||
}
|
||||
@@ -1,85 +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 MONTECARLO_REDUCTION_CUH
|
||||
#define MONTECARLO_REDUCTION_CUH
|
||||
|
||||
#include <cooperative_groups.h>
|
||||
|
||||
#include "MonteCarlo_common.cuh"
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This function calculates total sum for each of the two input arrays.
|
||||
// SUM_N must be power of two
|
||||
// Unrolling provides a bit of a performance improvement for small
|
||||
// to medium path counts.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, int SUM_N, int blockSize>
|
||||
__device__ void
|
||||
sumReduce(T* sum, T* sum2, cg::thread_block& cta, cg::thread_block_tile<32>& tile32, __TOptionValue* d_CallValue)
|
||||
{
|
||||
const int VEC = 32;
|
||||
const int tid = cta.thread_rank();
|
||||
|
||||
T beta = sum[tid];
|
||||
T beta2 = sum2[tid];
|
||||
T temp, temp2;
|
||||
|
||||
for (int i = VEC / 2; i > 0; i >>= 1)
|
||||
{
|
||||
if (tile32.thread_rank() < i)
|
||||
{
|
||||
temp = sum[tid + i];
|
||||
temp2 = sum2[tid + i];
|
||||
beta += temp;
|
||||
beta2 += temp2;
|
||||
sum[tid] = beta;
|
||||
sum2[tid] = beta2;
|
||||
}
|
||||
cg::sync(tile32);
|
||||
}
|
||||
cg::sync(cta);
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
beta = 0;
|
||||
beta2 = 0;
|
||||
for (int i = 0; i < blockDim.x; i += VEC)
|
||||
{
|
||||
beta += sum[i];
|
||||
beta2 += sum2[i];
|
||||
}
|
||||
__TOptionValue t = {beta, beta2};
|
||||
*d_CallValue = t;
|
||||
}
|
||||
cg::sync(cta);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,75 +0,0 @@
|
||||
# MonteCarloMultiGPU - Monte Carlo Option Pricing with Multi-GPU support
|
||||
|
||||
## Description
|
||||
|
||||
This sample evaluates fair call price for a given set of European options using the Monte Carlo approach, taking advantage of all CUDA-capable GPUs installed in the system. This sample use double precision hardware if a GTX 200 class GPU is present. The sample also takes advantage of CUDA 4.0 capability to supporting using a single CPU thread to control multiple GPUs
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Random Number Generator, Computational Finance, CURAND Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 3.5 ](https://developer.nvidia.com/cuda-gpus) [SM 3.7 ](https://developer.nvidia.com/cuda-gpus) [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)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, ppc64le, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemset, cudaFree, cudaStreamDestroy, cudaEventRecord, cudaMallocHost, cudaStreamCreate, cudaEventCreate, cudaGetDeviceCount, cudaDeviceSynchronize, cudaEventSynchronize, cudaFreeHost, cudaMalloc, cudaEventDestroy, cudaSetDevice, cudaMemcpyAsync, cudaStreamSynchronize, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CURAND](../../../README.md#curand)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit 11.6](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## Build and Run
|
||||
|
||||
### Windows
|
||||
The Windows samples are built using the Visual Studio IDE. Solution files (.sln) are provided for each supported version of Visual Studio, using the format:
|
||||
```
|
||||
*_vs<version>.sln - for Visual Studio <version>
|
||||
```
|
||||
Each individual sample has its own set of solution files in its directory:
|
||||
|
||||
To build/examine all the samples at once, the complete solution files should be used. To build/examine a single sample, the individual sample solution files should be used.
|
||||
> **Note:** Some samples require that the Microsoft DirectX SDK (June 2010 or newer) be installed and that the VC++ directory paths are properly set up (**Tools > Options...**). Check DirectX Dependencies section for details."
|
||||
|
||||
### Linux
|
||||
The Linux samples are built using makefiles. To use the makefiles, change the current directory to the sample directory you wish to build, and run make:
|
||||
```
|
||||
$ cd <sample_dir>
|
||||
$ make
|
||||
```
|
||||
The samples makefiles can take advantage of certain options:
|
||||
* **TARGET_ARCH=<arch>** - cross-compile targeting a specific architecture. Allowed architectures are x86_64, ppc64le, armv7l.
|
||||
By default, TARGET_ARCH is set to HOST_ARCH. On a x86_64 machine, not setting TARGET_ARCH is the equivalent of setting TARGET_ARCH=x86_64.<br/>
|
||||
`$ make TARGET_ARCH=x86_64` <br/> `$ make TARGET_ARCH=ppc64le` <br/> `$ make TARGET_ARCH=armv7l` <br/>
|
||||
See [here](http://docs.nvidia.com/cuda/cuda-samples/index.html#cross-samples) for more details.
|
||||
* **dbg=1** - build with debug symbols
|
||||
```
|
||||
$ make dbg=1
|
||||
```
|
||||
* **SMS="A B ..."** - override the SM architectures for which the sample will be built, where `"A B ..."` is a space-delimited list of SM architectures. For example, to generate SASS for SM 50 and SM 60, use `SMS="50 60"`.
|
||||
```
|
||||
$ make SMS="50 60"
|
||||
```
|
||||
|
||||
* **HOST_COMPILER=<host_compiler>** - override the default g++ host compiler. See the [Linux Installation Guide](http://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#system-requirements) for a list of supported host compilers.
|
||||
```
|
||||
$ make HOST_COMPILER=g++
|
||||
```
|
||||
|
||||
## References (for more details)
|
||||
|
||||
[whitepaper](./doc/MonteCarlo.pdf)
|
||||
@@ -1,49 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDASTF in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/* 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 REALTYPE_H
|
||||
#define REALTYPE_H
|
||||
|
||||
// #define DOUBLE_PRECISION
|
||||
|
||||
#ifndef DOUBLE_PRECISION
|
||||
typedef float real;
|
||||
#else
|
||||
typedef double real;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user