mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-13 00:08:26 +08:00
CUDA 13.2 samples update (#432)
- Added Python samples for CUDA Python 1.0 release - Renamed top-level `Samples` directory to `cpp` to accommodate Python samples.
This commit is contained in:
34
cpp/4_CUDA_Libraries/CMakeLists.txt
Normal file
34
cpp/4_CUDA_Libraries/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
add_subdirectory(FilterBorderControlNPP)
|
||||
add_subdirectory(MersenneTwisterGP11213)
|
||||
add_subdirectory(batchCUBLAS)
|
||||
add_subdirectory(boxFilterNPP)
|
||||
add_subdirectory(cannyEdgeDetectorNPP)
|
||||
add_subdirectory(conjugateGradient)
|
||||
add_subdirectory(conjugateGradientCudaGraphs)
|
||||
add_subdirectory(conjugateGradientMultiBlockCG)
|
||||
add_subdirectory(conjugateGradientMultiDeviceCG)
|
||||
add_subdirectory(conjugateGradientPrecond)
|
||||
add_subdirectory(conjugateGradientUM)
|
||||
add_subdirectory(cudaNvSci)
|
||||
add_subdirectory(cuSolverDn_LinearSolver)
|
||||
add_subdirectory(cuSolverRf)
|
||||
add_subdirectory(cuSolverSp_LinearSolver)
|
||||
add_subdirectory(cuSolverSp_LowlevelCholesky)
|
||||
add_subdirectory(cuSolverSp_LowlevelQR)
|
||||
add_subdirectory(freeImageInteropNPP)
|
||||
add_subdirectory(histEqualizationNPP)
|
||||
add_subdirectory(jitLto)
|
||||
add_subdirectory(lineOfSight)
|
||||
add_subdirectory(matrixMulCUBLAS)
|
||||
add_subdirectory(nvJPEG)
|
||||
add_subdirectory(nvJPEG_encoder)
|
||||
add_subdirectory(oceanFFT)
|
||||
add_subdirectory(randomFog)
|
||||
add_subdirectory(simpleCUBLAS)
|
||||
add_subdirectory(simpleCUBLASXT)
|
||||
add_subdirectory(simpleCUBLAS_LU)
|
||||
add_subdirectory(simpleCUFFT)
|
||||
add_subdirectory(simpleCUFFT_2d_MGPU)
|
||||
add_subdirectory(simpleCUFFT_MGPU)
|
||||
add_subdirectory(simpleCUFFT_callback)
|
||||
add_subdirectory(watershedSegmentationNPP)
|
||||
18
cpp/4_CUDA_Libraries/FilterBorderControlNPP/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/FilterBorderControlNPP/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/FilterBorderControlNPP/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/FilterBorderControlNPP/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
74
cpp/4_CUDA_Libraries/FilterBorderControlNPP/CMakeLists.txt
Normal file
74
cpp/4_CUDA_Libraries/FilterBorderControlNPP/CMakeLists.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(FilterBorderControlNPP LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(
|
||||
../../../Common
|
||||
../../../Common/UtilNPP
|
||||
)
|
||||
|
||||
# Source file
|
||||
find_package(FreeImage)
|
||||
|
||||
if(${FreeImage_FOUND})
|
||||
# Add target for FilterBorderControlNPP
|
||||
add_executable(FilterBorderControlNPP FilterBorderControlNPP.cpp)
|
||||
|
||||
target_compile_options(FilterBorderControlNPP PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(FilterBorderControlNPP PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(FilterBorderControlNPP PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(FilterBorderControlNPP PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${FreeImage_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(FilterBorderControlNPP PRIVATE
|
||||
CUDA::nppc
|
||||
CUDA::nppisu
|
||||
CUDA::nppif
|
||||
CUDA::nppitc
|
||||
CUDA::nppidei
|
||||
CUDA::nppial
|
||||
CUDA::cudart
|
||||
${FreeImage_LIBRARIES}
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET FilterBorderControlNPP POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||
${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
)
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET FilterBorderControlNPP
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${FreeImage_LIBRARY}/../FreeImage.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "FreeImage not found - will not build sample 'FilterBorderControlNPP'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
@@ -0,0 +1,602 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// USING NPP IMAGE FILTER SOURCE IMAGE BORDER CONTROL
|
||||
// This sample demonstrates how any border version of an NPP filtering function
|
||||
// can be used in the most common mode, with border control enabled. Mentioned
|
||||
// functions can be used to duplicate the results of the equivalent non-border
|
||||
// version of the NPP functions. They can be also used for enabling and
|
||||
// disabling border control on various source image edges depending on what
|
||||
// portion of the source image is being used as input.
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#pragma warning(disable : 4819)
|
||||
#endif
|
||||
|
||||
#include <Exceptions.h>
|
||||
#include <ImageIO.h>
|
||||
#include <ImagesCPU.h>
|
||||
#include <ImagesNPP.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <fstream>
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_string.h>
|
||||
#include <iostream>
|
||||
#include <npp.h>
|
||||
#include <string.h>
|
||||
|
||||
inline int cudaDeviceInit(int argc, const char **argv)
|
||||
{
|
||||
int deviceCount;
|
||||
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
|
||||
|
||||
if (deviceCount == 0) {
|
||||
std::cerr << "CUDA error: no devices supporting CUDA." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int dev = findCudaDevice(argc, argv);
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, dev);
|
||||
std::cerr << "cudaSetDevice GPU" << dev << " = " << deviceProp.name << std::endl;
|
||||
|
||||
checkCudaErrors(cudaSetDevice(dev));
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
try {
|
||||
const char *inputFile = "teapot512.pgm";
|
||||
std::string sFilename = inputFile;
|
||||
std::string sOutputDir = "./";
|
||||
|
||||
cudaDeviceInit(argc, (const char **)argv);
|
||||
|
||||
NppStreamContext nppStreamCtx;
|
||||
nppStreamCtx.hStream =
|
||||
0; // The NULL stream by default, set this to whatever your stream ID is if not the NULL stream.
|
||||
|
||||
cudaError_t cudaError = cudaGetDevice(&nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess) {
|
||||
printf("CUDA error: no devices supporting CUDA.\n");
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
}
|
||||
|
||||
const NppLibraryVersion *libVer = nppGetLibVersion();
|
||||
|
||||
printf("NPP Library Version %d.%d.%d\n", libVer->major, libVer->minor, libVer->build);
|
||||
|
||||
int driverVersion, runtimeVersion;
|
||||
cudaDriverGetVersion(&driverVersion);
|
||||
cudaRuntimeGetVersion(&runtimeVersion);
|
||||
|
||||
printf("CUDA Driver Version: %d.%d\n", driverVersion / 1000, (driverVersion % 100) / 10);
|
||||
printf("CUDA Runtime Version: %d.%d\n\n", runtimeVersion / 1000, (runtimeVersion % 100) / 10);
|
||||
|
||||
cudaError = cudaDeviceGetAttribute(&nppStreamCtx.nCudaDevAttrComputeCapabilityMajor,
|
||||
cudaDevAttrComputeCapabilityMajor,
|
||||
nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess)
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
|
||||
cudaError = cudaDeviceGetAttribute(&nppStreamCtx.nCudaDevAttrComputeCapabilityMinor,
|
||||
cudaDevAttrComputeCapabilityMinor,
|
||||
nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess)
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
|
||||
cudaError = cudaStreamGetFlags(nppStreamCtx.hStream, &nppStreamCtx.nStreamFlags);
|
||||
|
||||
cudaDeviceProp oDeviceProperties;
|
||||
|
||||
cudaError = cudaGetDeviceProperties(&oDeviceProperties, nppStreamCtx.nCudaDeviceId);
|
||||
|
||||
nppStreamCtx.nMultiProcessorCount = oDeviceProperties.multiProcessorCount;
|
||||
nppStreamCtx.nMaxThreadsPerMultiProcessor = oDeviceProperties.maxThreadsPerMultiProcessor;
|
||||
nppStreamCtx.nMaxThreadsPerBlock = oDeviceProperties.maxThreadsPerBlock;
|
||||
nppStreamCtx.nSharedMemPerBlock = oDeviceProperties.sharedMemPerBlock;
|
||||
|
||||
char *filePath;
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "input")) {
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "input", &filePath);
|
||||
}
|
||||
else {
|
||||
filePath = sdkFindFilePath(inputFile, argv[0]);
|
||||
}
|
||||
|
||||
if (!filePath) {
|
||||
std::cerr << "Couldn't find input file " << sFilename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sFilename = filePath;
|
||||
|
||||
// if we specify the filename at the command line, then we only test
|
||||
// sFilename[0].
|
||||
int file_errors = 0;
|
||||
std::ifstream infile(sFilename.data(), std::ifstream::in);
|
||||
|
||||
if (infile.good()) {
|
||||
std::cout << "gradientFilterBorderNPP opened <" << sFilename.data() << "> successfully!" << std::endl;
|
||||
file_errors = 0;
|
||||
infile.close();
|
||||
}
|
||||
else {
|
||||
std::cout << "gradientFilterBorderNPP unable to open <" << sFilename.data() << ">" << std::endl;
|
||||
file_errors++;
|
||||
infile.close();
|
||||
}
|
||||
|
||||
if (file_errors > 0) {
|
||||
cudaDeviceReset();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string sResultBaseFilename = sFilename;
|
||||
|
||||
std::string::size_type dot = sResultBaseFilename.rfind('.');
|
||||
|
||||
if (dot != std::string::npos) {
|
||||
sResultBaseFilename = sResultBaseFilename.substr(0, dot);
|
||||
}
|
||||
|
||||
std::string sResultXFilename = sOutputDir + sFilename + "_gradientVectorPrewittBorderX_Vertical.pgm";
|
||||
std::string sResultYFilename = sResultBaseFilename;
|
||||
|
||||
// sResultXFilename += "_gradientVectorPrewittBorderX_Vertical.pgm";
|
||||
sResultYFilename += "_gradientVectorPrewittBorderY_Horizontal.pgm";
|
||||
|
||||
// if (checkCmdLineFlag(argc, (const char **)argv, "output"))
|
||||
// {
|
||||
// char *outputFilePath;
|
||||
// getCmdLineArgumentString(argc, (const char **)argv, "output",
|
||||
// &outputFilePath); sResultBaseFilename = outputFilePath;
|
||||
// }
|
||||
|
||||
// declare a host image object for an 8-bit grayscale image
|
||||
npp::ImageCPU_8u_C1 oHostSrc;
|
||||
// load gray-scale image from disk
|
||||
npp::loadImage(sFilename, oHostSrc);
|
||||
// declare a device image and copy construct from the host image,
|
||||
// i.e. upload host to device
|
||||
npp::ImageNPP_8u_C1 oDeviceSrc(oHostSrc);
|
||||
|
||||
NppiSize oSrcSize = {(int)oDeviceSrc.width(), (int)oDeviceSrc.height()};
|
||||
NppiPoint oSrcOffset = {0, 0};
|
||||
|
||||
// create struct with ROI size
|
||||
NppiSize oSizeROI = {(int)oDeviceSrc.width(), (int)oDeviceSrc.height()};
|
||||
// allocate device destination images of appropriatedly size
|
||||
npp::ImageNPP_16s_C1 oDeviceDstX(oSizeROI.width, oSizeROI.height);
|
||||
npp::ImageNPP_16s_C1 oDeviceDstY(oSizeROI.width, oSizeROI.height);
|
||||
|
||||
// run Prewitt edge detection gradient vector filter
|
||||
NPP_CHECK_NPP(nppiGradientVectorPrewittBorder_8u16s_C1R_Ctx(oDeviceSrc.data(),
|
||||
oDeviceSrc.pitch(),
|
||||
oSrcSize,
|
||||
oSrcOffset,
|
||||
oDeviceDstX.data(),
|
||||
oDeviceDstX.pitch(),
|
||||
oDeviceDstY.data(),
|
||||
oDeviceDstY.pitch(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
oSizeROI,
|
||||
NPP_MASK_SIZE_3_X_3,
|
||||
nppiNormL1,
|
||||
NPP_BORDER_REPLICATE,
|
||||
nppStreamCtx));
|
||||
|
||||
// allocate device destination images of appropriatedly size
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutX(oSizeROI.width, oSizeROI.height);
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutY(oSizeROI.width, oSizeROI.height);
|
||||
|
||||
// convert 16s_C1 result images to binary 8u_C1 output images using constant
|
||||
// value to adjust amount of visible detail
|
||||
NPP_CHECK_NPP(nppiCompareC_16s_C1R_Ctx(oDeviceDstX.data(),
|
||||
oDeviceDstX.pitch(),
|
||||
32,
|
||||
oDeviceDstOutX.data(),
|
||||
oDeviceDstOutX.pitch(),
|
||||
oSizeROI,
|
||||
NPP_CMP_GREATER_EQ,
|
||||
nppStreamCtx));
|
||||
|
||||
NPP_CHECK_NPP(nppiCompareC_16s_C1R_Ctx(oDeviceDstY.data(),
|
||||
oDeviceDstY.pitch(),
|
||||
32,
|
||||
oDeviceDstOutY.data(),
|
||||
oDeviceDstOutY.pitch(),
|
||||
oSizeROI,
|
||||
NPP_CMP_GREATER_EQ,
|
||||
nppStreamCtx));
|
||||
|
||||
// create host images for the results
|
||||
npp::ImageCPU_8u_C1 oHostDstX(oDeviceDstOutX.size());
|
||||
npp::ImageCPU_8u_C1 oHostDstY(oDeviceDstOutY.size());
|
||||
// and copy the device result data into them
|
||||
oDeviceDstOutX.copyTo(oHostDstX.data(), oHostDstX.pitch());
|
||||
oDeviceDstOutY.copyTo(oHostDstY.data(), oHostDstY.pitch());
|
||||
|
||||
saveImage(sResultXFilename, oHostDstX);
|
||||
std::cout << "Saved image: " << sResultXFilename << std::endl;
|
||||
saveImage(sResultYFilename, oHostDstY);
|
||||
std::cout << "Saved image: " << sResultYFilename << std::endl;
|
||||
|
||||
// now use the Prewitt gradient border filter function in such a way that no
|
||||
// border replication operations will be applied
|
||||
|
||||
// create a Prewitt filter mask size object, Prewitt uses a 3x3 filter
|
||||
// kernel
|
||||
NppiSize oMaskSize = {3, 3};
|
||||
// create a size object for the enlarged source image
|
||||
NppiSize oEnlargedSrcSize = {oSrcSize.width + oMaskSize.width - 1, oSrcSize.height + oMaskSize.height - 1};
|
||||
|
||||
// create an enlarged device source image
|
||||
npp::ImageNPP_8u_C1 oEnlargedDeviceSrc(oEnlargedSrcSize.width, oEnlargedSrcSize.height);
|
||||
|
||||
// copy and enlarge the original device source image and surround it with a
|
||||
// white edge (border)
|
||||
NPP_CHECK_NPP(nppiCopyConstBorder_8u_C1R_Ctx(oDeviceSrc.data(),
|
||||
oDeviceSrc.pitch(),
|
||||
oSrcSize,
|
||||
oEnlargedDeviceSrc.data(),
|
||||
oEnlargedDeviceSrc.pitch(),
|
||||
oEnlargedSrcSize,
|
||||
oMaskSize.width / 2,
|
||||
oMaskSize.height / 2,
|
||||
255,
|
||||
nppStreamCtx));
|
||||
|
||||
// adjust oEnlargedDeviceSrc pixel pointer to point to the first pixel of
|
||||
// the original source image in the enlarged source image
|
||||
const Npp8u *pTemp = reinterpret_cast<const Npp8u *>(oEnlargedDeviceSrc.data());
|
||||
pTemp += (oMaskSize.height / 2) * oEnlargedDeviceSrc.pitch();
|
||||
const Npp8u *pAdjustedSrc = reinterpret_cast<const Npp8u *>((void *)(pTemp));
|
||||
pAdjustedSrc += oMaskSize.width / 2;
|
||||
|
||||
// create device output images for the no source border results
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutXNoBorders(oSizeROI.width, oSizeROI.height);
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutYNoBorders(oSizeROI.width, oSizeROI.height);
|
||||
|
||||
// tell the filter function what cartesian pixel position pAdjustedSrc is
|
||||
// pointing to within the enlarged source image
|
||||
oSrcOffset.x += oMaskSize.width / 2;
|
||||
oSrcOffset.y += oMaskSize.height / 2;
|
||||
|
||||
// run Prewitt edge detection gradient vector filter bypassing border
|
||||
// control due to enlarged source image
|
||||
NPP_CHECK_NPP(nppiGradientVectorPrewittBorder_8u16s_C1R_Ctx(pAdjustedSrc,
|
||||
oEnlargedDeviceSrc.pitch(),
|
||||
oEnlargedSrcSize,
|
||||
oSrcOffset,
|
||||
oDeviceDstX.data(),
|
||||
oDeviceDstX.pitch(),
|
||||
oDeviceDstY.data(),
|
||||
oDeviceDstY.pitch(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
oSizeROI,
|
||||
NPP_MASK_SIZE_3_X_3,
|
||||
nppiNormL1,
|
||||
NPP_BORDER_REPLICATE,
|
||||
nppStreamCtx));
|
||||
|
||||
// convert 16s_C1 result images to binary 8u_C1 output images using constant
|
||||
// value to adjust amount of visible detail
|
||||
NPP_CHECK_NPP(nppiCompareC_16s_C1R_Ctx(oDeviceDstX.data(),
|
||||
oDeviceDstX.pitch(),
|
||||
32,
|
||||
oDeviceDstOutXNoBorders.data(),
|
||||
oDeviceDstOutXNoBorders.pitch(),
|
||||
oSizeROI,
|
||||
NPP_CMP_GREATER_EQ,
|
||||
nppStreamCtx));
|
||||
|
||||
NPP_CHECK_NPP(nppiCompareC_16s_C1R_Ctx(oDeviceDstY.data(),
|
||||
oDeviceDstY.pitch(),
|
||||
32,
|
||||
oDeviceDstOutYNoBorders.data(),
|
||||
oDeviceDstOutYNoBorders.pitch(),
|
||||
oSizeROI,
|
||||
NPP_CMP_GREATER_EQ,
|
||||
nppStreamCtx));
|
||||
// create additional output files
|
||||
std::string sResultXNoBordersFilename = sResultBaseFilename;
|
||||
std::string sResultYNoBordersFilename = sResultBaseFilename;
|
||||
|
||||
sResultXNoBordersFilename += "_gradientVectorPrewittBorderX_Vertical_WithNoSourceBorders.pgm";
|
||||
sResultYNoBordersFilename += "_gradientVectorPrewittBorderY_Horizontal_WithNoSourceBorders.pgm";
|
||||
|
||||
// copy the device result data into the host output images
|
||||
oDeviceDstOutXNoBorders.copyTo(oHostDstX.data(), oHostDstX.pitch());
|
||||
oDeviceDstOutYNoBorders.copyTo(oHostDstY.data(), oHostDstY.pitch());
|
||||
|
||||
saveImage(sResultXNoBordersFilename, oHostDstX);
|
||||
std::cout << "Saved image: " << sResultXNoBordersFilename << std::endl;
|
||||
saveImage(sResultYNoBordersFilename, oHostDstY);
|
||||
std::cout << "Saved image: " << sResultYNoBordersFilename << std::endl;
|
||||
|
||||
// now diff the two output images, one using border control and one
|
||||
// bypassing border control
|
||||
|
||||
// create device output images for the diff results
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutXDiff(oSizeROI.width, oSizeROI.height);
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutYDiff(oSizeROI.width, oSizeROI.height);
|
||||
|
||||
// diff the two 8u_C1 result images one with and one without border control
|
||||
|
||||
NPP_CHECK_NPP(nppiAbsDiff_8u_C1R_Ctx(oDeviceDstOutXNoBorders.data(),
|
||||
oDeviceDstOutXNoBorders.pitch(),
|
||||
oDeviceDstOutX.data(),
|
||||
oDeviceDstOutX.pitch(),
|
||||
oDeviceDstOutXDiff.data(),
|
||||
oDeviceDstOutXDiff.pitch(),
|
||||
oSizeROI,
|
||||
nppStreamCtx));
|
||||
|
||||
NPP_CHECK_NPP(nppiAbsDiff_8u_C1R_Ctx(oDeviceDstOutYNoBorders.data(),
|
||||
oDeviceDstOutYNoBorders.pitch(),
|
||||
oDeviceDstOutY.data(),
|
||||
oDeviceDstOutY.pitch(),
|
||||
oDeviceDstOutYDiff.data(),
|
||||
oDeviceDstOutYDiff.pitch(),
|
||||
oSizeROI,
|
||||
nppStreamCtx));
|
||||
|
||||
// create additional output files
|
||||
std::string sResultXDiffFilename = sResultBaseFilename;
|
||||
std::string sResultYDiffFilename = sResultBaseFilename;
|
||||
|
||||
sResultXDiffFilename += "_gradientVectorPrewittBorderX_Vertical_BorderDiffs.pgm";
|
||||
sResultYDiffFilename += "_gradientVectorPrewittBorderY_Horizontal_BorderDiffs.pgm";
|
||||
|
||||
// copy the device result data into the host output images
|
||||
oDeviceDstOutXDiff.copyTo(oHostDstX.data(), oHostDstX.pitch());
|
||||
oDeviceDstOutYDiff.copyTo(oHostDstY.data(), oHostDstY.pitch());
|
||||
|
||||
saveImage(sResultXDiffFilename, oHostDstX);
|
||||
std::cout << "Saved image: " << sResultXDiffFilename << std::endl;
|
||||
saveImage(sResultYDiffFilename, oHostDstY);
|
||||
std::cout << "Saved image: " << sResultYDiffFilename << std::endl;
|
||||
|
||||
// if you closely examine the above difference files (recommend using GIMP
|
||||
// for viewing using scaling with no interpolation) you will see several
|
||||
// single pixel differences (white pixels) along the right and bottom edges
|
||||
// of the default vs. borderless images this happens because border pixels
|
||||
// in the original source image are duplicated when the filter kernels
|
||||
// overlap the edge of the source image when using the first version of the
|
||||
// filter call but are actually sampled from the enlarged source image when
|
||||
// using the second version of the filter call the technique used in the
|
||||
// second filter call can be used with any filter border function in NPP to
|
||||
// duplicate results that would be generated from a non-border filter
|
||||
// function call by filling the border pixel outside the embedded source
|
||||
// image with the appropriate border pixel values
|
||||
|
||||
// here is how to use border control to process a source image in multiple
|
||||
// calls and get correct output in the destination image
|
||||
|
||||
// since the source image pointer already points to the beginning of the
|
||||
// source image in the enlarged source image it doesn't need changed
|
||||
|
||||
// tighten up the top and left source image borders - this will enable
|
||||
// border replication on the left and top borders of the original source
|
||||
// image
|
||||
oSrcOffset.x = 0;
|
||||
oSrcOffset.y = 0;
|
||||
// tighten up the right and bottom side source image borders - this will
|
||||
// enable border replication on the right and bottom borders of the original
|
||||
// source image
|
||||
oEnlargedSrcSize.width = oSrcSize.width;
|
||||
oEnlargedSrcSize.height = oSrcSize.height;
|
||||
|
||||
// create device output images for the mixed edge results
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutXMixedBorders(oSizeROI.width, oSizeROI.height);
|
||||
npp::ImageNPP_8u_C1 oDeviceDstOutYMixedBorders(oSizeROI.width, oSizeROI.height);
|
||||
|
||||
// shrink output ROI width so that only the left half of the destination
|
||||
// image will be generated however since oEnlargedSrcSize.width is still set
|
||||
// to oSrcSize.width then border control will be disabled when the filter
|
||||
// needs to access source pixels beyond the right side of the left half of
|
||||
// the source image
|
||||
int nLeftWidth = oSizeROI.width / 2;
|
||||
int nRightWidth = oSizeROI.width - nLeftWidth;
|
||||
oSizeROI.width = nLeftWidth;
|
||||
|
||||
// run Prewitt edge detection gradient vector filter to generate the left
|
||||
// side of the output image
|
||||
NPP_CHECK_NPP(nppiGradientVectorPrewittBorder_8u16s_C1R_Ctx(pAdjustedSrc,
|
||||
oEnlargedDeviceSrc.pitch(),
|
||||
oEnlargedSrcSize,
|
||||
oSrcOffset,
|
||||
oDeviceDstX.data(),
|
||||
oDeviceDstX.pitch(),
|
||||
oDeviceDstY.data(),
|
||||
oDeviceDstY.pitch(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
oSizeROI,
|
||||
NPP_MASK_SIZE_3_X_3,
|
||||
nppiNormL1,
|
||||
NPP_BORDER_REPLICATE,
|
||||
nppStreamCtx));
|
||||
|
||||
// now move the enlarged source pointer to the horizontal middle of the
|
||||
// enlarged source image and tell the function where it was moved to
|
||||
pAdjustedSrc += nLeftWidth;
|
||||
// and adjust the source offset parameter accordingly - this will in effect
|
||||
// turn off border control for the left border allowing the necessary source
|
||||
// pixels to be used
|
||||
oSrcOffset.x += nLeftWidth;
|
||||
|
||||
// update oSizeROI.width so that only enough destination pixels will be
|
||||
// produced to fill the right half of the destination image
|
||||
oSizeROI.width = nRightWidth;
|
||||
|
||||
// run Prewitt edge detection gradient vector filter to generate the right
|
||||
// side of the output image adjusting the destination image pointers
|
||||
// appropriately
|
||||
NPP_CHECK_NPP(nppiGradientVectorPrewittBorder_8u16s_C1R_Ctx(pAdjustedSrc,
|
||||
oEnlargedDeviceSrc.pitch(),
|
||||
oEnlargedSrcSize,
|
||||
oSrcOffset,
|
||||
oDeviceDstX.data() + nLeftWidth,
|
||||
oDeviceDstX.pitch(),
|
||||
oDeviceDstY.data() + nLeftWidth,
|
||||
oDeviceDstY.pitch(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
oSizeROI,
|
||||
NPP_MASK_SIZE_3_X_3,
|
||||
nppiNormL1,
|
||||
NPP_BORDER_REPLICATE,
|
||||
nppStreamCtx));
|
||||
|
||||
// convert 16s_C1 result images to binary 8u_C1 output images using constant
|
||||
// value to adjust amount of visible detail
|
||||
NPP_CHECK_NPP(nppiCompareC_16s_C1R_Ctx(oDeviceDstX.data(),
|
||||
oDeviceDstX.pitch(),
|
||||
32,
|
||||
oDeviceDstOutXMixedBorders.data(),
|
||||
oDeviceDstOutXMixedBorders.pitch(),
|
||||
oSizeROI,
|
||||
NPP_CMP_GREATER_EQ,
|
||||
nppStreamCtx));
|
||||
|
||||
NPP_CHECK_NPP(nppiCompareC_16s_C1R_Ctx(oDeviceDstY.data(),
|
||||
oDeviceDstY.pitch(),
|
||||
32,
|
||||
oDeviceDstOutYMixedBorders.data(),
|
||||
oDeviceDstOutYMixedBorders.pitch(),
|
||||
oSizeROI,
|
||||
NPP_CMP_GREATER_EQ,
|
||||
nppStreamCtx));
|
||||
// create additional output files
|
||||
std::string sResultXMixedBordersFilename = sResultBaseFilename;
|
||||
std::string sResultYMixedBordersFilename = sResultBaseFilename;
|
||||
|
||||
sResultXMixedBordersFilename += "_gradientVectorPrewittBorderX_Vertical_WithMixedBorders.pgm";
|
||||
sResultYMixedBordersFilename += "_gradientVectorPrewittBorderY_Horizontal_WithMixedBorders.pgm";
|
||||
|
||||
// copy the device result data into the host output images
|
||||
oDeviceDstOutXMixedBorders.copyTo(oHostDstX.data(), oHostDstX.pitch());
|
||||
oDeviceDstOutYMixedBorders.copyTo(oHostDstY.data(), oHostDstY.pitch());
|
||||
|
||||
saveImage(sResultXMixedBordersFilename, oHostDstX);
|
||||
std::cout << "Saved image: " << sResultXMixedBordersFilename << std::endl;
|
||||
saveImage(sResultYMixedBordersFilename, oHostDstY);
|
||||
std::cout << "Saved image: " << sResultYMixedBordersFilename << std::endl;
|
||||
|
||||
// diff the original 8u_C1 result images with border control and the mixed
|
||||
// border control images, they should match (diff image will be all black)
|
||||
|
||||
NPP_CHECK_NPP(nppiAbsDiff_8u_C1R_Ctx(oDeviceDstOutXMixedBorders.data(),
|
||||
oDeviceDstOutXMixedBorders.pitch(),
|
||||
oDeviceDstOutX.data(),
|
||||
oDeviceDstOutX.pitch(),
|
||||
oDeviceDstOutXDiff.data(),
|
||||
oDeviceDstOutXDiff.pitch(),
|
||||
oSizeROI,
|
||||
nppStreamCtx));
|
||||
|
||||
NPP_CHECK_NPP(nppiAbsDiff_8u_C1R_Ctx(oDeviceDstOutYMixedBorders.data(),
|
||||
oDeviceDstOutYMixedBorders.pitch(),
|
||||
oDeviceDstOutY.data(),
|
||||
oDeviceDstOutY.pitch(),
|
||||
oDeviceDstOutYDiff.data(),
|
||||
oDeviceDstOutYDiff.pitch(),
|
||||
oSizeROI,
|
||||
nppStreamCtx));
|
||||
|
||||
// create additional output files
|
||||
std::string sResultXMixedDiffFilename = sResultBaseFilename;
|
||||
std::string sResultYMixedDiffFilename = sResultBaseFilename;
|
||||
|
||||
sResultXMixedDiffFilename += "_gradientVectorPrewittBorderX_Vertical_MixedBorderDiffs.pgm";
|
||||
sResultYMixedDiffFilename += "_gradientVectorPrewittBorderY_Horizontal_MixedBorderDiffs.pgm";
|
||||
|
||||
// copy the device result data into the host output images
|
||||
oDeviceDstOutXDiff.copyTo(oHostDstX.data(), oHostDstX.pitch());
|
||||
oDeviceDstOutYDiff.copyTo(oHostDstY.data(), oHostDstY.pitch());
|
||||
|
||||
saveImage(sResultXMixedDiffFilename, oHostDstX);
|
||||
std::cout << "Saved image: " << sResultXMixedDiffFilename << std::endl;
|
||||
saveImage(sResultYMixedDiffFilename, oHostDstY);
|
||||
std::cout << "Saved image: " << sResultYMixedDiffFilename << std::endl;
|
||||
|
||||
nppiFree(oDeviceSrc.data());
|
||||
nppiFree(oDeviceDstX.data());
|
||||
nppiFree(oDeviceDstY.data());
|
||||
nppiFree(oDeviceDstOutX.data());
|
||||
nppiFree(oDeviceDstOutY.data());
|
||||
nppiFree(oDeviceDstOutXNoBorders.data());
|
||||
nppiFree(oDeviceDstOutYNoBorders.data());
|
||||
nppiFree(oDeviceDstOutXDiff.data());
|
||||
nppiFree(oDeviceDstOutYDiff.data());
|
||||
nppiFree(oDeviceDstOutXMixedBorders.data());
|
||||
nppiFree(oDeviceDstOutYMixedBorders.data());
|
||||
nppiFree(oEnlargedDeviceSrc.data());
|
||||
|
||||
cudaDeviceReset();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
catch (npp::Exception &rException) {
|
||||
std::cerr << "Program error! The following exception occurred: \n";
|
||||
std::cerr << rException << std::endl;
|
||||
std::cerr << "Aborting." << std::endl;
|
||||
|
||||
cudaDeviceReset();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Program error! An unknow type of exception occurred. \n";
|
||||
std::cerr << "Aborting." << std::endl;
|
||||
|
||||
cudaDeviceReset();
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
cpp/4_CUDA_Libraries/FilterBorderControlNPP/README.md
Normal file
36
cpp/4_CUDA_Libraries/FilterBorderControlNPP/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# FilterBorderControlNPP - Filter Border Control NPP
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates how any border version of an NPP filtering function can be used in the most common mode, with border control enabled. Mentioned functions can be used to duplicate the results of the equivalent non-border version of the NPP functions. They can be also used for enabling and disabling border control on various source image edges depending on what portion of the source image is being used as input.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Performance Strategies, Image Processing, NPP Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaRuntimeGetVersion, cudaDeviceReset, cudaSetDevice, cudaGetDeviceCount, cudaDeviceInit, cudaDriverGetVersion, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[FreeImage](../../../README.md#freeimage), [NPP](../../../README.md#npp)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
BIN
cpp/4_CUDA_Libraries/FilterBorderControlNPP/data/teapot512.pgm
Normal file
BIN
cpp/4_CUDA_Libraries/FilterBorderControlNPP/data/teapot512.pgm
Normal file
Binary file not shown.
18
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
43
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/CMakeLists.txt
Normal file
43
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(MersenneTwisterGP11213 LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for MersenneTwisterGP11213
|
||||
add_executable(MersenneTwisterGP11213 MersenneTwister.cpp)
|
||||
|
||||
target_compile_options(MersenneTwisterGP11213 PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(MersenneTwisterGP11213 PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(MersenneTwisterGP11213 PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(MersenneTwisterGP11213 PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(MersenneTwisterGP11213 PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::curand
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
179
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/MersenneTwister.cpp
Normal file
179
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/MersenneTwister.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/* 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 the use of CURAND to generate
|
||||
* random numbers on GPU and CPU.
|
||||
*/
|
||||
|
||||
// Utilities and system includes
|
||||
// includes, system
|
||||
#include <curand.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <cuda_runtime.h>
|
||||
#include <curand.h>
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_functions.h>
|
||||
|
||||
float compareResults(int rand_n, float *h_RandGPU, float *h_RandCPU);
|
||||
|
||||
const int DEFAULT_RAND_N = 2400000;
|
||||
const unsigned int DEFAULT_SEED = 777;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Start logs
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
// initialize the GPU, either identified by --device
|
||||
// or by picking the device with highest flop rate.
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
// parsing the number of random numbers to generate
|
||||
int rand_n = DEFAULT_RAND_N;
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "count")) {
|
||||
rand_n = getCmdLineArgumentInt(argc, (const char **)argv, "count");
|
||||
}
|
||||
|
||||
printf("Allocating data for %i samples...\n", rand_n);
|
||||
|
||||
// parsing the seed
|
||||
int seed = DEFAULT_SEED;
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "seed")) {
|
||||
seed = getCmdLineArgumentInt(argc, (const char **)argv, "seed");
|
||||
}
|
||||
|
||||
printf("Seeding with %i ...\n", seed);
|
||||
|
||||
cudaStream_t stream;
|
||||
checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
|
||||
|
||||
float *d_Rand;
|
||||
checkCudaErrors(cudaMalloc((void **)&d_Rand, rand_n * sizeof(float)));
|
||||
|
||||
curandGenerator_t prngGPU;
|
||||
checkCudaErrors(curandCreateGenerator(&prngGPU, CURAND_RNG_PSEUDO_MTGP32));
|
||||
checkCudaErrors(curandSetStream(prngGPU, stream));
|
||||
checkCudaErrors(curandSetPseudoRandomGeneratorSeed(prngGPU, seed));
|
||||
|
||||
curandGenerator_t prngCPU;
|
||||
checkCudaErrors(curandCreateGeneratorHost(&prngCPU, CURAND_RNG_PSEUDO_MTGP32));
|
||||
checkCudaErrors(curandSetPseudoRandomGeneratorSeed(prngCPU, seed));
|
||||
|
||||
//
|
||||
// Example 1: Compare random numbers generated on GPU and CPU
|
||||
float *h_RandGPU;
|
||||
checkCudaErrors(cudaMallocHost(&h_RandGPU, rand_n * sizeof(float)));
|
||||
|
||||
printf("Generating random numbers on GPU...\n\n");
|
||||
checkCudaErrors(curandGenerateUniform(prngGPU, (float *)d_Rand, rand_n));
|
||||
|
||||
printf("\nReading back the results...\n");
|
||||
checkCudaErrors(cudaMemcpyAsync(h_RandGPU, d_Rand, rand_n * sizeof(float), cudaMemcpyDeviceToHost, stream));
|
||||
|
||||
float *h_RandCPU = (float *)malloc(rand_n * sizeof(float));
|
||||
|
||||
printf("Generating random numbers on CPU...\n\n");
|
||||
checkCudaErrors(curandGenerateUniform(prngCPU, (float *)h_RandCPU, rand_n));
|
||||
|
||||
checkCudaErrors(cudaStreamSynchronize(stream));
|
||||
printf("Comparing CPU/GPU random numbers...\n\n");
|
||||
float L1norm = compareResults(rand_n, h_RandGPU, h_RandCPU);
|
||||
|
||||
//
|
||||
// Example 2: Timing of random number generation on GPU
|
||||
const int numIterations = 10;
|
||||
int i;
|
||||
StopWatchInterface *hTimer;
|
||||
|
||||
sdkCreateTimer(&hTimer);
|
||||
sdkResetTimer(&hTimer);
|
||||
sdkStartTimer(&hTimer);
|
||||
|
||||
for (i = 0; i < numIterations; i++) {
|
||||
checkCudaErrors(curandGenerateUniform(prngGPU, (float *)d_Rand, rand_n));
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaStreamSynchronize(stream));
|
||||
sdkStopTimer(&hTimer);
|
||||
|
||||
double gpuTime = 1.0e-3 * sdkGetTimerValue(&hTimer) / (double)numIterations;
|
||||
|
||||
printf("MersenneTwisterGP11213, Throughput = %.4f GNumbers/s, Time = %.5f s, "
|
||||
"Size = %u Numbers\n",
|
||||
1.0e-9 * rand_n / gpuTime,
|
||||
gpuTime,
|
||||
rand_n);
|
||||
|
||||
printf("Shutting down...\n");
|
||||
|
||||
checkCudaErrors(curandDestroyGenerator(prngGPU));
|
||||
checkCudaErrors(curandDestroyGenerator(prngCPU));
|
||||
checkCudaErrors(cudaStreamDestroy(stream));
|
||||
checkCudaErrors(cudaFree(d_Rand));
|
||||
sdkDeleteTimer(&hTimer);
|
||||
checkCudaErrors(cudaFreeHost(h_RandGPU));
|
||||
free(h_RandCPU);
|
||||
|
||||
exit(L1norm < 1e-6 ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float compareResults(int rand_n, float *h_RandGPU, float *h_RandCPU)
|
||||
{
|
||||
int i;
|
||||
float rCPU, rGPU, delta;
|
||||
float max_delta = 0.;
|
||||
float sum_delta = 0.;
|
||||
float sum_ref = 0.;
|
||||
|
||||
for (i = 0; i < rand_n; i++) {
|
||||
rCPU = h_RandCPU[i];
|
||||
rGPU = h_RandGPU[i];
|
||||
delta = fabs(rCPU - rGPU);
|
||||
sum_delta += delta;
|
||||
sum_ref += fabs(rCPU);
|
||||
|
||||
if (delta >= max_delta) {
|
||||
max_delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
float L1norm = (float)(sum_delta / sum_ref);
|
||||
printf("Max absolute error: %E\n", max_delta);
|
||||
printf("L1 norm: %E\n\n", L1norm);
|
||||
|
||||
return L1norm;
|
||||
}
|
||||
36
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/README.md
Normal file
36
cpp/4_CUDA_Libraries/MersenneTwisterGP11213/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# MersenneTwisterGP11213 - MersenneTwisterGP11213
|
||||
|
||||
## Description
|
||||
|
||||
This sample demonstrates the Mersenne Twister random number generator GP11213 in cuRAND.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
CURAND Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaStreamCreateWithFlags, cudaStreamDestroy, cudaFree, cudaMallocHost, cudaFreeHost, cudaStreamSynchronize, cudaMalloc, cudaMemcpyAsync
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CURAND](../../../README.md#curand)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
101
cpp/4_CUDA_Libraries/README.md
Normal file
101
cpp/4_CUDA_Libraries/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# 4. CUDA Libraries
|
||||
|
||||
|
||||
### [batchCUBLAS](./batchCUBLAS)
|
||||
A CUDA Sample that demonstrates how using batched CUBLAS API calls to improve overall performance.
|
||||
|
||||
### [boxFilterNPP](./boxFilterNPP)
|
||||
A NPP CUDA Sample that demonstrates how to use NPP FilterBox function to perform a Box Filter.
|
||||
|
||||
### [cannyEdgeDetectorNPP](./cannyEdgeDetectorNPP)
|
||||
An NPP CUDA Sample that demonstrates the recommended parameters to use with the nppiFilterCannyBorder_8u_C1R Canny Edge Detection image filter function. This function expects a single channel 8-bit grayscale input image. You can generate a grayscale image from a color image by first calling nppiColorToGray() or nppiRGBToGray(). The Canny Edge Detection function combines and improves on the techniques required to produce an edge detection image using multiple steps.
|
||||
|
||||
### [conjugateGradient](./conjugateGradient)
|
||||
This sample implements a conjugate gradient solver on GPU using CUBLAS and CUSPARSE library.
|
||||
|
||||
### [conjugateGradientCudaGraphs](./conjugateGradientCudaGraphs)
|
||||
This sample implements a conjugate gradient solver on GPU using CUBLAS and CUSPARSE library calls captured and called using CUDA Graph APIs.
|
||||
|
||||
### [conjugateGradientMultiBlockCG](./conjugateGradientMultiBlockCG)
|
||||
This sample implements a conjugate gradient solver on GPU using Multi Block Cooperative Groups, also uses Unified Memory.
|
||||
|
||||
### [conjugateGradientMultiDeviceCG](./conjugateGradientMultiDeviceCG)
|
||||
This sample implements a conjugate gradient solver on multiple GPUs using Multi Device Cooperative Groups, also uses Unified Memory optimized using prefetching and usage hints.
|
||||
|
||||
### [conjugateGradientPrecond](./conjugateGradientPrecond)
|
||||
This sample implements a preconditioned conjugate gradient solver on GPU using CUBLAS and CUSPARSE library.
|
||||
|
||||
### [conjugateGradientUM](./conjugateGradientUM)
|
||||
This sample implements a conjugate gradient solver on GPU using CUBLAS and CUSPARSE library, using Unified Memory
|
||||
|
||||
### [cudaNvSci](./cudaNvSci)
|
||||
This sample demonstrates CUDA-NvSciBuf/NvSciSync Interop. Two CPU threads import the NvSciBuf and NvSciSync into CUDA to perform two image processing algorithms on a ppm image - image rotation in 1st thread & rgba to grayscale conversion of rotated image in 2nd thread. Currently only supported on Ubuntu 18.04
|
||||
|
||||
### [cuSolverDn_LinearSolver](./cuSolverDn_LinearSolver)
|
||||
A CUDA Sample that demonstrates cuSolverDN's LU, QR and Cholesky factorization.
|
||||
|
||||
### [cuSolverRf](./cuSolverRf)
|
||||
A CUDA Sample that demonstrates cuSolver's refactorization library - CUSOLVERRF.
|
||||
|
||||
### [cuSolverSp_LinearSolver](./cuSolverSp_LinearSolver)
|
||||
A CUDA Sample that demonstrates cuSolverSP's LU, QR and Cholesky factorization.
|
||||
|
||||
### [cuSolverSp_LowlevelCholesky](./cuSolverSp_LowlevelCholesky)
|
||||
A CUDA Sample that demonstrates Cholesky factorization using cuSolverSP's low level APIs.
|
||||
|
||||
### [cuSolverSp_LowlevelQR](./cuSolverSp_LowlevelQR)
|
||||
A CUDA Sample that demonstrates QR factorization using cuSolverSP's low level APIs.
|
||||
|
||||
### [FilterBorderControlNPP](./FilterBorderControlNPP)
|
||||
This sample demonstrates how any border version of an NPP filtering function can be used in the most common mode, with border control enabled. Mentioned functions can be used to duplicate the results of the equivalent non-border version of the NPP functions. They can be also used for enabling and disabling border control on various source image edges depending on what portion of the source image is being used as input.
|
||||
|
||||
### [freeImageInteropNPP](./freeImageInteropNPP)
|
||||
A simple CUDA Sample demonstrate how to use FreeImage library with NPP.
|
||||
|
||||
### [histEqualizationNPP](./histEqualizationNPP)
|
||||
This CUDA Sample demonstrates how to use NPP for histogram equalization for image data.
|
||||
|
||||
### [lineOfSight](./lineOfSight)
|
||||
This sample is an implementation of a simple line-of-sight algorithm: Given a height map and a ray originating at some observation point, it computes all the points along the ray that are visible from the observation point. The implementation is based on the Thrust library.
|
||||
|
||||
### [matrixMulCUBLAS](./matrixMulCUBLAS)
|
||||
This sample implements matrix multiplication from Chapter 3 of the programming guide. To illustrate GPU performance for matrix multiply, this sample also shows how to use the new CUDA 4.0 interface for CUBLAS to demonstrate high-performance performance for matrix multiplication.
|
||||
|
||||
### [MersenneTwisterGP11213](./MersenneTwisterGP11213)
|
||||
This sample demonstrates the Mersenne Twister random number generator GP11213 in cuRAND.
|
||||
|
||||
### [nvJPEG](./nvJPEG)
|
||||
A CUDA Sample that demonstrates single and batched decoding of jpeg images using NVJPEG Library.
|
||||
|
||||
### [nvJPEG_encoder](./nvJPEG_encoder)
|
||||
A CUDA Sample that demonstrates single encoding of jpeg images using NVJPEG Library.
|
||||
|
||||
### [oceanFFT](./oceanFFT)
|
||||
This sample simulates an Ocean height field using CUFFT Library and renders the result using OpenGL.
|
||||
|
||||
### [randomFog](./randomFog)
|
||||
This sample illustrates pseudo- and quasi- random numbers produced by CURAND.
|
||||
|
||||
### [simpleCUBLAS](./simpleCUBLAS)
|
||||
Example of using CUBLAS API interface to perform GEMM operations.
|
||||
|
||||
### [simpleCUBLAS_LU](./simpleCUBLAS_LU)
|
||||
CUDA sample demonstrating cuBLAS API cublasDgetrfBatched() for lower-upper (LU) decomposition of a matrix.
|
||||
|
||||
### [simpleCUBLASXT](./simpleCUBLASXT)
|
||||
Example of using CUBLAS-XT library which performs GEMM operations over Multiple GPUs.
|
||||
|
||||
### [simpleCUFFT](./simpleCUFFT)
|
||||
Example of using CUFFT. In this example, CUFFT is used to compute the 1D-convolution of some signal with some filter by transforming both into frequency domain, multiplying them together, and transforming the signal back to time domain. cuFFT plans are created using simple and advanced API functions.
|
||||
|
||||
### [simpleCUFFT_2d_MGPU](./simpleCUFFT_2d_MGPU)
|
||||
Example of using CUFFT. In this example, CUFFT is used to compute the 2D-convolution of some signal with some filter by transforming both into frequency domain, multiplying them together, and transforming the signal back to time domain on Multiple GPU.
|
||||
|
||||
### [simpleCUFFT_callback](./simpleCUFFT_callback)
|
||||
Example of using CUFFT. In this example, CUFFT is used to compute the 1D-convolution of some signal with some filter by transforming both into frequency domain, multiplying them together, and transforming the signal back to time domain. The difference between this example and the Simple CUFFT example is that the multiplication step is done by the CUFFT kernel with a user-supplied CUFFT callback routine, rather than by a separate kernel call.
|
||||
|
||||
### [simpleCUFFT_MGPU](./simpleCUFFT_MGPU)
|
||||
Example of using CUFFT. In this example, CUFFT is used to compute the 1D-convolution of some signal with some filter by transforming both into frequency domain, multiplying them together, and transforming the signal back to time domain on Multiple GPU.
|
||||
|
||||
### [watershedSegmentationNPP](./watershedSegmentationNPP)
|
||||
An NPP CUDA Sample that demonstrates how to use the NPP watershed segmentation function.
|
||||
18
cpp/4_CUDA_Libraries/batchCUBLAS/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/batchCUBLAS/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/batchCUBLAS/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/batchCUBLAS/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
43
cpp/4_CUDA_Libraries/batchCUBLAS/CMakeLists.txt
Normal file
43
cpp/4_CUDA_Libraries/batchCUBLAS/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(batchCUBLAS LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for batchCUBLAS
|
||||
add_executable(batchCUBLAS batchCUBLAS.cpp)
|
||||
|
||||
target_compile_options(batchCUBLAS PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(batchCUBLAS PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(batchCUBLAS PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(batchCUBLAS PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(batchCUBLAS PRIVATE
|
||||
CUDA::cublas
|
||||
CUDA::cudart
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
39
cpp/4_CUDA_Libraries/batchCUBLAS/README.md
Normal file
39
cpp/4_CUDA_Libraries/batchCUBLAS/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# batchCUBLAS - batchCUBLAS
|
||||
|
||||
## Description
|
||||
|
||||
A CUDA Sample that demonstrates how using batched CUBLAS API calls to improve overall performance.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUBLAS Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuRand, cuEqual
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaGetErrorString, cudaFree, cudaGetLastError, cudaDeviceSynchronize, cudaGetDevice, cudaMalloc, cudaStreamCreate, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUBLAS](../../../README.md#cublas)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
727
cpp/4_CUDA_Libraries/batchCUBLAS/batchCUBLAS.cpp
Normal file
727
cpp/4_CUDA_Libraries/batchCUBLAS/batchCUBLAS.cpp
Normal file
@@ -0,0 +1,727 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This example demonstrates how to get better performance by
|
||||
* batching CUBLAS calls with the use of using streams
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
/* Using updated (v2) interfaces to cublas and cusparse */
|
||||
#include <cublas_v2.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <helper_cuda.h>
|
||||
|
||||
#include "batchCUBLAS.h"
|
||||
|
||||
const char *sSDKname = "batchCUBLAS";
|
||||
|
||||
//==============================================================================
|
||||
// Device information utilities
|
||||
//==============================================================================
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int getDeviceVersion(void)
|
||||
{
|
||||
int device;
|
||||
struct cudaDeviceProp properties;
|
||||
|
||||
if (cudaGetDevice(&device) != cudaSuccess) {
|
||||
printf("failed to get device\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cudaGetDeviceProperties(&properties, device) != cudaSuccess) {
|
||||
printf("failed to get properties\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return properties.major * 100 + properties.minor * 10;
|
||||
}
|
||||
|
||||
size_t getDeviceMemory(void)
|
||||
{
|
||||
struct cudaDeviceProp properties;
|
||||
int device;
|
||||
|
||||
if (cudaGetDevice(&device) != cudaSuccess) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cudaGetDeviceProperties(&properties, device) != cudaSuccess) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return properties.totalGlobalMem;
|
||||
}
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
//==============================================================================
|
||||
// random utilities
|
||||
//==============================================================================
|
||||
|
||||
template <typename T_ELEM> void fillupMatrix(T_ELEM *A, int lda, int rows, int cols, int seed = 0);
|
||||
|
||||
template <typename T_ELEM> void fillupMatrix(T_ELEM *A, int lda, int rows, int cols, int seed)
|
||||
{
|
||||
for (int j = 0; j < cols; j++) {
|
||||
for (int i = 0; i < rows; i++) {
|
||||
A[i + lda * j] = cuGet<T_ELEM>(((double)(((lda * i + j + seed) % 253) + 1)) / 256.0,
|
||||
((double)((((cols * i + j) + 123 + seed) % 253) + 1)) / 256.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Explicit instantiation */
|
||||
template void fillupMatrix<float>(float *A, int lda, int rows, int cols, int seed);
|
||||
template void fillupMatrix<double>(double *A, int lda, int rows, int cols, int seed);
|
||||
|
||||
/* For debugging */
|
||||
void printCuType(const char *str, float A) { fprintf(stdout, "%s (0x%08x, %g)", str, floatAsUInt(A), A); }
|
||||
|
||||
void printCuType(const char *str, double A) { fprintf(stdout, "%s (0x%016llx, %g)", str, doubleAsULL(A), A); }
|
||||
|
||||
//==============================================================================
|
||||
// defines and structures
|
||||
//==============================================================================
|
||||
|
||||
#define CUBLAS_SGEMM_MAX_ULP_ERR (.3)
|
||||
#define CUBLAS_DGEMM_MAX_ULP_ERR (1.e-3)
|
||||
#define CUBLAS_SGEMM_MAX_RELATIVE_ERR (6.e-6)
|
||||
#define CUBLAS_DGEMM_MAX_RELATIVE_ERR (0.0)
|
||||
#define CUBLAS_GEMM_TEST_COUNT (30)
|
||||
#define BENCH_MATRIX_M (128)
|
||||
#define BENCH_MATRIX_K (128)
|
||||
#define BENCH_MATRIX_N (128)
|
||||
|
||||
#define CLEANUP() \
|
||||
do { \
|
||||
if (A) \
|
||||
free(A); \
|
||||
if (B) \
|
||||
free(B); \
|
||||
if (C) \
|
||||
free(C); \
|
||||
for (int i = 0; i < opts.N; ++i) { \
|
||||
if (devPtrA[i]) \
|
||||
cudaFree(devPtrA[i]); \
|
||||
if (devPtrB[i]) \
|
||||
cudaFree(devPtrB[i]); \
|
||||
if (devPtrC[i]) \
|
||||
cudaFree(devPtrC[i]); \
|
||||
} \
|
||||
if (devPtrA) \
|
||||
free(devPtrA); \
|
||||
if (devPtrB) \
|
||||
free(devPtrB); \
|
||||
if (devPtrC) \
|
||||
free(devPtrC); \
|
||||
if (devPtrA_dev) \
|
||||
cudaFree(devPtrA_dev); \
|
||||
if (devPtrB_dev) \
|
||||
cudaFree(devPtrB_dev); \
|
||||
if (devPtrC_dev) \
|
||||
cudaFree(devPtrC_dev); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
enum testMethod { tmRegular, tmStream, tmBatched };
|
||||
|
||||
struct gemmOpts
|
||||
{
|
||||
int m;
|
||||
int n;
|
||||
int k;
|
||||
testMethod test_method;
|
||||
char *elem_type;
|
||||
int N; // number of multiplications
|
||||
};
|
||||
|
||||
template <typename T_ELEM> struct gemmTestParams
|
||||
{
|
||||
cublasOperation_t transa;
|
||||
cublasOperation_t transb;
|
||||
int m;
|
||||
int n;
|
||||
int k;
|
||||
T_ELEM alpha;
|
||||
T_ELEM beta;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// template wrappers for cuda functions
|
||||
//==============================================================================
|
||||
|
||||
static inline cublasStatus_t cublasXgemm(cublasHandle_t handle,
|
||||
cublasOperation_t transa,
|
||||
cublasOperation_t transb,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
float *alpha,
|
||||
const float *A,
|
||||
int lda,
|
||||
float *B,
|
||||
int ldb,
|
||||
float *beta,
|
||||
float *C,
|
||||
int ldc)
|
||||
{
|
||||
return cublasSgemm(handle, transa, transb, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
|
||||
}
|
||||
|
||||
static inline cublasStatus_t cublasXgemm(cublasHandle_t handle,
|
||||
cublasOperation_t transa,
|
||||
cublasOperation_t transb,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
double *alpha,
|
||||
const double *A,
|
||||
int lda,
|
||||
double *B,
|
||||
int ldb,
|
||||
double *beta,
|
||||
double *C,
|
||||
int ldc)
|
||||
{
|
||||
return cublasDgemm(handle, transa, transb, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
|
||||
}
|
||||
|
||||
static inline cublasStatus_t cublasXgemmBatched(cublasHandle_t handle,
|
||||
cublasOperation_t transa,
|
||||
cublasOperation_t transb,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
float *alpha,
|
||||
const float *Aarray[],
|
||||
int lda,
|
||||
const float *Barray[],
|
||||
int ldb,
|
||||
float *beta,
|
||||
float *Carray[],
|
||||
int ldc,
|
||||
int batchCount)
|
||||
{
|
||||
#if CUDART_VERSION >= 4010
|
||||
return cublasSgemmBatched(
|
||||
handle, transa, transb, m, n, k, alpha, Aarray, lda, Barray, ldb, beta, Carray, ldc, batchCount);
|
||||
#else
|
||||
return CUBLAS_STATUS_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline cublasStatus_t cublasXgemmBatched(cublasHandle_t handle,
|
||||
cublasOperation_t transa,
|
||||
cublasOperation_t transb,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
double *alpha,
|
||||
const double *Aarray[],
|
||||
int lda,
|
||||
const double *Barray[],
|
||||
int ldb,
|
||||
double *beta,
|
||||
double *Carray[],
|
||||
int ldc,
|
||||
int batchCount)
|
||||
{
|
||||
#if CUDART_VERSION >= 4010
|
||||
return cublasDgemmBatched(
|
||||
handle, transa, transb, m, n, k, alpha, Aarray, lda, Barray, ldb, beta, Carray, ldc, batchCount);
|
||||
#else
|
||||
return CUBLAS_STATUS_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Primary Application code
|
||||
//==============================================================================
|
||||
|
||||
static int processArgs(int argc, char *argv[], struct gemmOpts *opts)
|
||||
{
|
||||
int error = 0;
|
||||
int oldError;
|
||||
memset(opts, 0, sizeof(*opts));
|
||||
static char default_type[] = "d"; // default double
|
||||
opts->elem_type = default_type;
|
||||
opts->N = 10;
|
||||
|
||||
while (argc) {
|
||||
oldError = error;
|
||||
|
||||
if (*argv[0] == SWITCH_CHAR) {
|
||||
switch (*(argv[0] + 1)) {
|
||||
case 'm':
|
||||
opts->m = (int)atol(argv[0] + 2);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
opts->n = (int)atol(argv[0] + 2);
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
opts->k = (int)atol(argv[0] + 2);
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
opts->N = (int)atol(argv[0] + 2);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error > oldError) {
|
||||
fprintf(stderr, "Invalid switch '%c%s'\n", SWITCH_CHAR, argv[0] + 1);
|
||||
}
|
||||
|
||||
argc -= 1;
|
||||
argv++;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
template <typename T_ELEM>
|
||||
static int TESTGEN(gemm)(const struct gemmOpts *opts,
|
||||
int matrixM,
|
||||
int matrixN,
|
||||
int matrixK,
|
||||
int &numTests,
|
||||
struct gemmTestParams<T_ELEM> *params)
|
||||
{
|
||||
static T_ELEM alpha[] = {
|
||||
cuGet<T_ELEM>(0, 0), cuGet<T_ELEM>(-1, -1), cuGet<T_ELEM>(1, -2), cuGet<T_ELEM>(2, -1), cuGet<T_ELEM>(0, -3)};
|
||||
static T_ELEM beta[] = {
|
||||
cuGet<T_ELEM>(0, 0), cuGet<T_ELEM>(-1, -1), cuGet<T_ELEM>(1, -2), cuGet<T_ELEM>(2, -1), cuGet<T_ELEM>(0, -3)};
|
||||
|
||||
#define NBR_ALPHAS (sizeof(alpha) / sizeof(alpha[0]))
|
||||
#define NBR_BETAS (sizeof(beta) / sizeof(beta[0]))
|
||||
static T_ELEM theAlpha;
|
||||
static T_ELEM theBeta;
|
||||
static int state;
|
||||
static int m;
|
||||
static int n;
|
||||
static int k;
|
||||
|
||||
if (numTests-- <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
theAlpha = alpha[cuRand() % NBR_ALPHAS];
|
||||
theBeta = beta[cuRand() % NBR_BETAS];
|
||||
params->transa = CUBLAS_OP_N;
|
||||
params->transb = CUBLAS_OP_N;
|
||||
m = matrixM;
|
||||
n = matrixN;
|
||||
k = matrixK;
|
||||
params->m = m;
|
||||
params->n = n;
|
||||
params->k = k;
|
||||
params->alpha = theAlpha;
|
||||
params->beta = theBeta;
|
||||
|
||||
printf("#### args: ta=%d tb=%d m=%d n=%d k=%d ",
|
||||
(unsigned int)params->transa,
|
||||
(unsigned int)params->transb,
|
||||
params->m,
|
||||
params->n,
|
||||
params->k);
|
||||
printCuType(" alpha =", params->alpha);
|
||||
printCuType(" beta=", params->beta);
|
||||
printf("\n");
|
||||
|
||||
m = cuRand() % matrixM;
|
||||
n = cuRand() % matrixN;
|
||||
k = cuRand() % matrixK;
|
||||
|
||||
state = cuRand() % 9;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T_ELEM> void fillupMatrixDebug(T_ELEM *A, int lda, int rows, int cols)
|
||||
{
|
||||
for (int j = 0; j < cols; j++) {
|
||||
for (int i = 0; i < rows; i++) {
|
||||
A[i + lda * j] = cuGet<T_ELEM>(i + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T_ELEM>
|
||||
int test_gemm_loop(struct gemmOpts &opts, float err, double max_relative_error, cublasHandle_t handle)
|
||||
{
|
||||
struct gemmTestParams<T_ELEM> params;
|
||||
cudaStream_t *streamArray = 0;
|
||||
cublasStatus_t status1, status2, status3;
|
||||
T_ELEM *A = NULL;
|
||||
T_ELEM *B = NULL;
|
||||
T_ELEM *C = NULL;
|
||||
T_ELEM **devPtrA = 0;
|
||||
T_ELEM **devPtrB = 0;
|
||||
T_ELEM **devPtrC = 0;
|
||||
T_ELEM **devPtrA_dev = NULL;
|
||||
T_ELEM **devPtrB_dev = NULL;
|
||||
T_ELEM **devPtrC_dev = NULL;
|
||||
int matrixM, matrixN, matrixK;
|
||||
int rowsA, rowsB, rowsC;
|
||||
int colsA, colsB, colsC;
|
||||
int matrixSizeA, matrixSizeB, matrixSizeC;
|
||||
int errors;
|
||||
double start, stop;
|
||||
|
||||
printf("Testing %cgemm\n", *opts.elem_type);
|
||||
|
||||
matrixM = (opts.m) ? opts.m : BENCH_MATRIX_M;
|
||||
matrixN = (opts.n) ? opts.n : BENCH_MATRIX_N;
|
||||
matrixK = (opts.k) ? opts.k : BENCH_MATRIX_K;
|
||||
|
||||
rowsA = imax(1, matrixM);
|
||||
colsA = imax(1, matrixK);
|
||||
rowsB = imax(1, matrixK);
|
||||
colsB = imax(1, matrixN);
|
||||
rowsC = imax(1, matrixM);
|
||||
colsC = imax(1, matrixN);
|
||||
|
||||
matrixSizeA = rowsA * colsA;
|
||||
matrixSizeB = rowsB * colsB;
|
||||
matrixSizeC = rowsC * colsC;
|
||||
|
||||
devPtrA = (T_ELEM **)malloc(opts.N * sizeof(*devPtrA));
|
||||
devPtrB = (T_ELEM **)malloc(opts.N * sizeof(*devPtrB));
|
||||
devPtrC = (T_ELEM **)malloc(opts.N * sizeof(*devPtrC));
|
||||
|
||||
for (int i = 0; i < opts.N; i++) {
|
||||
cudaError_t err1 = cudaMalloc((void **)&devPtrA[i], matrixSizeA * sizeof(devPtrA[0][0]));
|
||||
cudaError_t err2 = cudaMalloc((void **)&devPtrB[i], matrixSizeB * sizeof(devPtrB[0][0]));
|
||||
cudaError_t err3 = cudaMalloc((void **)&devPtrC[i], matrixSizeC * sizeof(devPtrC[0][0]));
|
||||
|
||||
if ((err1 != cudaSuccess) || (err2 != cudaSuccess) || (err3 != cudaSuccess)) {
|
||||
CLEANUP();
|
||||
fprintf(stderr, "!!!! GPU memory allocation error\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
// For batched processing we need those arrays on the device
|
||||
if (opts.test_method == tmBatched) {
|
||||
cudaError_t err1 = cudaMalloc((void **)&devPtrA_dev, opts.N * sizeof(*devPtrA));
|
||||
cudaError_t err2 = cudaMalloc((void **)&devPtrB_dev, opts.N * sizeof(*devPtrB));
|
||||
cudaError_t err3 = cudaMalloc((void **)&devPtrC_dev, opts.N * sizeof(*devPtrC));
|
||||
|
||||
if ((err1 != cudaSuccess) || (err2 != cudaSuccess) || (err3 != cudaSuccess)) {
|
||||
CLEANUP();
|
||||
fprintf(stderr, "!!!! GPU memory allocation error\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
|
||||
err1 = cudaMemcpy(devPtrA_dev, devPtrA, opts.N * sizeof(*devPtrA), cudaMemcpyHostToDevice);
|
||||
err2 = cudaMemcpy(devPtrB_dev, devPtrB, opts.N * sizeof(*devPtrB), cudaMemcpyHostToDevice);
|
||||
err3 = cudaMemcpy(devPtrC_dev, devPtrC, opts.N * sizeof(*devPtrC), cudaMemcpyHostToDevice);
|
||||
|
||||
if ((err1 != cudaSuccess) || (err2 != cudaSuccess) || (err3 != cudaSuccess)) {
|
||||
CLEANUP();
|
||||
fprintf(stderr, "!!!! cannot copy pointer array to device\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
A = (T_ELEM *)malloc(matrixSizeA * sizeof(A[0]));
|
||||
B = (T_ELEM *)malloc(matrixSizeB * sizeof(B[0]));
|
||||
C = (T_ELEM *)malloc(matrixSizeC * sizeof(C[0]));
|
||||
|
||||
if ((!A) || (!B) || (!C)) {
|
||||
CLEANUP();
|
||||
fprintf(stderr, "!!!! system memory allocation error\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
|
||||
streamArray = (cudaStream_t *)malloc(opts.N * sizeof(cudaStream_t *));
|
||||
|
||||
for (int i = 0; i < opts.N; i++) {
|
||||
if (opts.test_method == tmStream) {
|
||||
cudaError_t cudaErr = cudaStreamCreate(&streamArray[i]);
|
||||
|
||||
if (cudaErr != cudaSuccess) {
|
||||
CLEANUP();
|
||||
fprintf(stderr, "!!!! cannot create stream\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
streamArray[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
errors = 0;
|
||||
int numTests = 1;
|
||||
|
||||
while (TESTGEN(gemm)(&opts, matrixM, matrixN, matrixK, numTests, ¶ms) == 0) {
|
||||
printf("#### args: lda=%d ldb=%d ldc=%d\n", rowsA, rowsB, rowsC);
|
||||
|
||||
// fillup with Nan first (so lda padding is full on Nan)
|
||||
memset(A, 0xFF, matrixSizeA * sizeof(A[0]));
|
||||
fillupMatrixDebug(A, rowsA, params.m, params.k);
|
||||
memset(B, 0xFF, matrixSizeB * sizeof(B[0]));
|
||||
fillupMatrix(B, rowsB, params.k, params.n, 121);
|
||||
|
||||
if (!cuEqual(params.beta, cuGet<T_ELEM>(0))) {
|
||||
fillupMatrix(C, rowsC, params.m, params.n);
|
||||
}
|
||||
else {
|
||||
/* fill with SNaNs to make sure ZGEMM doesn't access C */
|
||||
memset(C, 0xFF, matrixSizeC * sizeof(C[0]));
|
||||
}
|
||||
|
||||
double flopsCoef = 2.0;
|
||||
|
||||
for (int i = 0; i < opts.N; i++) {
|
||||
status1 = cublasSetMatrix(rowsA, colsA, sizeof(A[0]), A, rowsA, devPtrA[i], rowsA);
|
||||
status2 = cublasSetMatrix(rowsB, colsB, sizeof(B[0]), B, rowsB, devPtrB[i], rowsB);
|
||||
status3 = cublasSetMatrix(rowsC, colsC, sizeof(C[0]), C, rowsC, devPtrC[i], rowsC);
|
||||
|
||||
if ((status1 != CUBLAS_STATUS_SUCCESS) || (status2 != status1) || (status3 != status1)) {
|
||||
CLEANUP();
|
||||
fprintf(stderr, "!!!! GPU access error (write)\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
start = second();
|
||||
|
||||
if (opts.test_method == tmBatched) {
|
||||
cublasSetStream(handle, streamArray[0]);
|
||||
status1 = cublasXgemmBatched(handle,
|
||||
params.transa,
|
||||
params.transb,
|
||||
params.m,
|
||||
params.n,
|
||||
params.k,
|
||||
¶ms.alpha,
|
||||
(const T_ELEM **)devPtrA_dev,
|
||||
rowsA,
|
||||
(const T_ELEM **)devPtrB_dev,
|
||||
rowsB,
|
||||
¶ms.beta,
|
||||
devPtrC_dev,
|
||||
rowsC,
|
||||
opts.N);
|
||||
|
||||
if (status1 != CUBLAS_STATUS_SUCCESS) {
|
||||
cudaError_t cudaStatus = cudaGetLastError();
|
||||
CLEANUP();
|
||||
fprintf(stderr,
|
||||
"!!!! GPU program execution error : cublas Error=%d, cuda "
|
||||
"Error=%d,(%s)\n",
|
||||
status1,
|
||||
cudaStatus,
|
||||
cudaGetErrorString(cudaStatus));
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < opts.N; i++) {
|
||||
cublasSetStream(handle, streamArray[i]);
|
||||
status1 = cublasXgemm(handle,
|
||||
params.transa,
|
||||
params.transb,
|
||||
params.m,
|
||||
params.n,
|
||||
params.k,
|
||||
¶ms.alpha,
|
||||
devPtrA[i],
|
||||
rowsA,
|
||||
devPtrB[i],
|
||||
rowsB,
|
||||
¶ms.beta,
|
||||
devPtrC[i],
|
||||
rowsC);
|
||||
|
||||
if (status1 != CUBLAS_STATUS_SUCCESS) {
|
||||
cudaError_t cudaStatus = cudaGetLastError();
|
||||
CLEANUP();
|
||||
fprintf(stderr,
|
||||
"!!!! GPU program execution error : cublas Error=%d, cuda "
|
||||
"Error=%d,(%s)\n",
|
||||
status1,
|
||||
cudaStatus,
|
||||
cudaGetErrorString(cudaStatus));
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cudaError_t cudaStatus = cudaDeviceSynchronize();
|
||||
|
||||
if (cudaStatus != cudaSuccess) {
|
||||
CLEANUP();
|
||||
fprintf(stderr,
|
||||
"!!!! GPU program execution error on cudaDeviceSynchronize : "
|
||||
"cudaError=%d,(%s)\n",
|
||||
cudaStatus,
|
||||
cudaGetErrorString(cudaStatus));
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
|
||||
stop = second();
|
||||
|
||||
fprintf(stdout,
|
||||
"^^^^ elapsed = %10.8f sec GFLOPS=%g\n",
|
||||
(stop - start),
|
||||
opts.N * (1e-9 * flopsCoef * params.m * params.n * params.k) / (stop - start));
|
||||
|
||||
} // end while (TESTGEN..
|
||||
|
||||
CLEANUP();
|
||||
fprintf(stdout, "@@@@ %cgemm test %s\n", *opts.elem_type, errors ? "FAIL" : "OK");
|
||||
return CUBLASTEST_PASSED;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct gemmOpts opts;
|
||||
int errors, nTimes, nTotalErrors = 0;
|
||||
int status = CUBLASTEST_PASSED;
|
||||
|
||||
printf("%s Starting...\n\n", sSDKname);
|
||||
|
||||
int dev = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (dev == -1) {
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
|
||||
errors = processArgs(argc, argv, &opts);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stdout,
|
||||
"\n Usage: batchcublas [-mSIZE_M] [-nSIZE_N] [-kSIZE_N] "
|
||||
"[-NSIZE_NUM_ITERATIONS] [-qatest] [-noprompt]\n");
|
||||
return CUBLASTEST_FAILED;
|
||||
}
|
||||
|
||||
cublasHandle_t handle;
|
||||
|
||||
if (cublasCreate(&handle) != CUBLAS_STATUS_SUCCESS) {
|
||||
fprintf(stdout, "CUBLAS initialization failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Run single kernels
|
||||
fprintf(stdout, "\n ==== Running single kernels ==== \n\n");
|
||||
nTimes = opts.N;
|
||||
opts.N = 1;
|
||||
*(opts.elem_type) = 's';
|
||||
status =
|
||||
test_gemm_loop<float>(opts, (float)CUBLAS_SGEMM_MAX_ULP_ERR, (double)CUBLAS_SGEMM_MAX_RELATIVE_ERR, handle);
|
||||
|
||||
// Run Double version
|
||||
*(opts.elem_type) = 'd';
|
||||
|
||||
if (getDeviceVersion() < DEV_VER_DBL_SUPPORT) {
|
||||
fprintf(stdout, "@@@@ dgemm test WAIVED due to lack of DP support\n");
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
|
||||
status =
|
||||
test_gemm_loop<double>(opts, (float)CUBLAS_DGEMM_MAX_ULP_ERR, (double)CUBLAS_DGEMM_MAX_RELATIVE_ERR, handle);
|
||||
nTotalErrors += (status == CUBLASTEST_PASSED ? 0 : 1);
|
||||
opts.N = nTimes;
|
||||
|
||||
// Run with and without streams and then batched. The batched functions are a
|
||||
// feature new feature in 4.1
|
||||
#if CUDART_VERSION >= 4010
|
||||
|
||||
for (int ii = 0; ii < 3; ii++) {
|
||||
#else
|
||||
|
||||
for (int ii = 0; ii < 2; ii++) {
|
||||
#endif
|
||||
|
||||
switch (ii) {
|
||||
case 0:
|
||||
opts.test_method = tmRegular;
|
||||
fprintf(stdout, "\n ==== Running N=%d without streams ==== \n\n", opts.N);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
opts.test_method = tmStream;
|
||||
fprintf(stdout, "\n ==== Running N=%d with streams ==== \n\n", opts.N);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
opts.test_method = tmBatched;
|
||||
fprintf(stdout, "\n ==== Running N=%d batched ==== \n\n", opts.N);
|
||||
break;
|
||||
}
|
||||
|
||||
// Run single version
|
||||
*(opts.elem_type) = 's';
|
||||
status =
|
||||
test_gemm_loop<float>(opts, (float)CUBLAS_SGEMM_MAX_ULP_ERR, (double)CUBLAS_SGEMM_MAX_RELATIVE_ERR, handle);
|
||||
nTotalErrors += (status == CUBLASTEST_PASSED ? 0 : 1);
|
||||
|
||||
// Run Double version
|
||||
*(opts.elem_type) = 'd';
|
||||
|
||||
// Test doesn't meet minSpec, will will wave the DP test
|
||||
if (getDeviceVersion() < DEV_VER_DBL_SUPPORT) {
|
||||
fprintf(stdout, "@@@@ dgemm test WAIVED due to lack of DP support\n");
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
else {
|
||||
status = test_gemm_loop<double>(
|
||||
opts, (float)CUBLAS_DGEMM_MAX_ULP_ERR, (double)CUBLAS_DGEMM_MAX_RELATIVE_ERR, handle);
|
||||
nTotalErrors += (status == CUBLASTEST_PASSED ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
cublasDestroy(handle);
|
||||
|
||||
printf("\nTest Summary\n");
|
||||
printf("%d error(s)\n", nTotalErrors);
|
||||
exit(nTotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
179
cpp/4_CUDA_Libraries/batchCUBLAS/batchCUBLAS.h
Normal file
179
cpp/4_CUDA_Libraries/batchCUBLAS/batchCUBLAS.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This example demonstrates how to get better performance by
|
||||
* batching CUBLAS calls with the use of using streams
|
||||
*/
|
||||
|
||||
#include <cublas_v2.h>
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SWITCH_CHAR '-'
|
||||
|
||||
#define REFFUNC(funcname) ref_##funcname
|
||||
#define TESTGEN(funcname) get_##funcname##_params
|
||||
#define TESTPARAMS(funcname) funcname##TestParams
|
||||
|
||||
#define DEV_VER_DBL_SUPPORT (130)
|
||||
#define DEV_VER_ALL_SUPPORT (999)
|
||||
|
||||
/* Errors Tests to be returned by all the Cublas test */
|
||||
#define CUBLASTEST_PASSED 0
|
||||
#define CUBLASTEST_FAILED 1
|
||||
#define CUBLASTEST_WAIVED 2
|
||||
|
||||
//==============================================================================
|
||||
// Device information utilities
|
||||
//==============================================================================
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
int getDeviceVersion(void);
|
||||
size_t getDeviceMemory(void);
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
//==============================================================================
|
||||
// Math utilities
|
||||
//==============================================================================
|
||||
|
||||
static __inline__ int imax(int x, int y) { return (x > y) ? x : y; }
|
||||
|
||||
static __inline__ unsigned floatAsUInt(float x)
|
||||
{
|
||||
volatile union
|
||||
{
|
||||
float f;
|
||||
unsigned i;
|
||||
} xx;
|
||||
xx.f = x;
|
||||
return xx.i;
|
||||
}
|
||||
|
||||
static __inline__ unsigned long long doubleAsULL(double x)
|
||||
{
|
||||
volatile union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} xx;
|
||||
xx.f = x;
|
||||
return xx.i;
|
||||
}
|
||||
|
||||
static __inline__ unsigned cuRand(void)
|
||||
{
|
||||
/* George Marsaglia's fast inline random number generator */
|
||||
#define CUDA_ZNEW (cuda_z = 36969 * (cuda_z & 65535) + (cuda_z >> 16))
|
||||
#define CUDA_WNEW (cuda_w = 18000 * (cuda_w & 65535) + (cuda_w >> 16))
|
||||
#define CUDA_MWC ((CUDA_ZNEW << 16) + CUDA_WNEW)
|
||||
#define CUDA_SHR3 \
|
||||
(cuda_jsr = cuda_jsr ^ (cuda_jsr << 17), \
|
||||
cuda_jsr = cuda_jsr ^ (cuda_jsr >> 13), \
|
||||
cuda_jsr = cuda_jsr ^ (cuda_jsr << 5))
|
||||
#define CUDA_CONG (cuda_jcong = 69069 * cuda_jcong + 1234567)
|
||||
#define KISS ((CUDA_MWC ^ CUDA_CONG) + CUDA_SHR3)
|
||||
static unsigned int cuda_z = 362436069, cuda_w = 521288629;
|
||||
static unsigned int cuda_jsr = 123456789, cuda_jcong = 380116160;
|
||||
return KISS;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// cuGet and cuEqual versions
|
||||
//==============================================================================
|
||||
|
||||
template <typename T_ELEM> __inline__ __device__ __host__ T_ELEM cuGet(double);
|
||||
template <> __inline__ __device__ __host__ float cuGet<float>(double x) { return float(x); }
|
||||
template <> __inline__ __device__ __host__ double cuGet<double>(double x) { return double(x); }
|
||||
|
||||
template <typename T_ELEM> __inline__ __device__ __host__ T_ELEM cuGet(double, double);
|
||||
template <> __inline__ __device__ __host__ float cuGet<float>(double x, double y) { return float(x); }
|
||||
template <> __inline__ __device__ __host__ double cuGet<double>(double x, double y) { return double(x); }
|
||||
static __inline__ __device__ __host__ bool cuEqual(float x, float y) { return (x == y); }
|
||||
static __inline__ __device__ __host__ bool cuEqual(double x, double y) { return (x == y); }
|
||||
|
||||
//==============================================================================
|
||||
// Platform dependent timing utility
|
||||
//==============================================================================
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
static __inline__ double second(void)
|
||||
{
|
||||
LARGE_INTEGER t;
|
||||
static double oofreq;
|
||||
static int checkedForHighResTimer;
|
||||
static BOOL hasHighResTimer;
|
||||
|
||||
if (!checkedForHighResTimer) {
|
||||
hasHighResTimer = QueryPerformanceFrequency(&t);
|
||||
oofreq = 1.0 / (double)t.QuadPart;
|
||||
checkedForHighResTimer = 1;
|
||||
}
|
||||
|
||||
if (hasHighResTimer) {
|
||||
QueryPerformanceCounter(&t);
|
||||
return (double)t.QuadPart * oofreq;
|
||||
}
|
||||
else {
|
||||
return (double)GetTickCount() / 1000.0;
|
||||
}
|
||||
}
|
||||
#elif defined(__linux__) || defined(__QNX__)
|
||||
#include <stddef.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
static double second(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include <stddef.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
static double second(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
||||
}
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
18
cpp/4_CUDA_Libraries/boxFilterNPP/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/boxFilterNPP/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/boxFilterNPP/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/boxFilterNPP/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
71
cpp/4_CUDA_Libraries/boxFilterNPP/CMakeLists.txt
Normal file
71
cpp/4_CUDA_Libraries/boxFilterNPP/CMakeLists.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(boxFilterNPP LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(
|
||||
../../../Common
|
||||
../../../Common/UtilNPP
|
||||
)
|
||||
|
||||
# Source file
|
||||
find_package(FreeImage)
|
||||
|
||||
if(${FreeImage_FOUND})
|
||||
# Add target for boxFilterNPP
|
||||
add_executable(boxFilterNPP boxFilterNPP.cpp)
|
||||
|
||||
target_compile_options(boxFilterNPP PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(boxFilterNPP PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(boxFilterNPP PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(boxFilterNPP PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${FreeImage_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(boxFilterNPP PRIVATE
|
||||
CUDA::nppc
|
||||
CUDA::nppisu
|
||||
CUDA::nppif
|
||||
CUDA::cudart
|
||||
${FreeImage_LIBRARIES}
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET boxFilterNPP POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/teapot512.pgm
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET boxFilterNPP
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${FreeImage_LIBRARY}/../FreeImage.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "FreeImage not found - will not build sample 'boxFilterNPP'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
36
cpp/4_CUDA_Libraries/boxFilterNPP/README.md
Normal file
36
cpp/4_CUDA_Libraries/boxFilterNPP/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# boxFilterNPP - Box Filter with NPP
|
||||
|
||||
## Description
|
||||
|
||||
A NPP CUDA Sample that demonstrates how to use NPP FilterBox function to perform a Box Filter.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Performance Strategies, Image Processing, NPP Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaRuntimeGetVersion, cudaDriverGetVersion
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[FreeImage](../../../README.md#freeimage), [NPP](../../../README.md#npp)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
213
cpp/4_CUDA_Libraries/boxFilterNPP/boxFilterNPP.cpp
Normal file
213
cpp/4_CUDA_Libraries/boxFilterNPP/boxFilterNPP.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#pragma warning(disable : 4819)
|
||||
#endif
|
||||
|
||||
#include <Exceptions.h>
|
||||
#include <ImageIO.h>
|
||||
#include <ImagesCPU.h>
|
||||
#include <ImagesNPP.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <fstream>
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_string.h>
|
||||
#include <iostream>
|
||||
#include <npp.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
try {
|
||||
std::string sFilename;
|
||||
char *filePath;
|
||||
NppStreamContext nppStreamCtx;
|
||||
nppStreamCtx.hStream =
|
||||
0; // The NULL stream by default, set this to whatever your stream ID is if not the NULL stream.
|
||||
|
||||
cudaError_t cudaError = cudaGetDevice(&nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess) {
|
||||
printf("CUDA error: no devices supporting CUDA.\n");
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
}
|
||||
|
||||
const NppLibraryVersion *libVer = nppGetLibVersion();
|
||||
|
||||
printf("NPP Library Version %d.%d.%d\n", libVer->major, libVer->minor, libVer->build);
|
||||
|
||||
int driverVersion, runtimeVersion;
|
||||
cudaDriverGetVersion(&driverVersion);
|
||||
cudaRuntimeGetVersion(&runtimeVersion);
|
||||
|
||||
printf("CUDA Driver Version: %d.%d\n", driverVersion / 1000, (driverVersion % 100) / 10);
|
||||
printf("CUDA Runtime Version: %d.%d\n\n", runtimeVersion / 1000, (runtimeVersion % 100) / 10);
|
||||
|
||||
cudaError = cudaDeviceGetAttribute(&nppStreamCtx.nCudaDevAttrComputeCapabilityMajor,
|
||||
cudaDevAttrComputeCapabilityMajor,
|
||||
nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess)
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
|
||||
cudaError = cudaDeviceGetAttribute(&nppStreamCtx.nCudaDevAttrComputeCapabilityMinor,
|
||||
cudaDevAttrComputeCapabilityMinor,
|
||||
nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess)
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
|
||||
cudaError = cudaStreamGetFlags(nppStreamCtx.hStream, &nppStreamCtx.nStreamFlags);
|
||||
|
||||
cudaDeviceProp oDeviceProperties;
|
||||
|
||||
cudaError = cudaGetDeviceProperties(&oDeviceProperties, nppStreamCtx.nCudaDeviceId);
|
||||
|
||||
nppStreamCtx.nMultiProcessorCount = oDeviceProperties.multiProcessorCount;
|
||||
nppStreamCtx.nMaxThreadsPerMultiProcessor = oDeviceProperties.maxThreadsPerMultiProcessor;
|
||||
nppStreamCtx.nMaxThreadsPerBlock = oDeviceProperties.maxThreadsPerBlock;
|
||||
nppStreamCtx.nSharedMemPerBlock = oDeviceProperties.sharedMemPerBlock;
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "input")) {
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "input", &filePath);
|
||||
}
|
||||
else {
|
||||
filePath = sdkFindFilePath("teapot512.pgm", argv[0]);
|
||||
}
|
||||
|
||||
if (filePath) {
|
||||
sFilename = filePath;
|
||||
}
|
||||
else {
|
||||
sFilename = "teapot512.pgm";
|
||||
}
|
||||
|
||||
// if we specify the filename at the command line, then we only test
|
||||
// sFilename[0].
|
||||
int file_errors = 0;
|
||||
std::ifstream infile(sFilename.data(), std::ifstream::in);
|
||||
|
||||
if (infile.good()) {
|
||||
std::cout << "boxFilterNPP opened: <" << sFilename.data() << "> successfully!" << std::endl;
|
||||
file_errors = 0;
|
||||
infile.close();
|
||||
}
|
||||
else {
|
||||
std::cout << "boxFilterNPP unable to open: <" << sFilename.data() << ">" << std::endl;
|
||||
file_errors++;
|
||||
infile.close();
|
||||
}
|
||||
|
||||
if (file_errors > 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string sResultFilename = sFilename;
|
||||
|
||||
std::string::size_type dot = sResultFilename.rfind('.');
|
||||
|
||||
if (dot != std::string::npos) {
|
||||
sResultFilename = sResultFilename.substr(0, dot);
|
||||
}
|
||||
|
||||
sResultFilename += "_boxFilter.pgm";
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "output")) {
|
||||
char *outputFilePath;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "output", &outputFilePath);
|
||||
sResultFilename = outputFilePath;
|
||||
}
|
||||
|
||||
// declare a host image object for an 8-bit grayscale image
|
||||
npp::ImageCPU_8u_C1 oHostSrc;
|
||||
// load gray-scale image from disk
|
||||
npp::loadImage(sFilename, oHostSrc);
|
||||
// declare a device image and copy construct from the host image,
|
||||
// i.e. upload host to device
|
||||
npp::ImageNPP_8u_C1 oDeviceSrc(oHostSrc);
|
||||
|
||||
// create struct with box-filter mask size
|
||||
NppiSize oMaskSize = {5, 5};
|
||||
|
||||
NppiSize oSrcSize = {(int)oDeviceSrc.width(), (int)oDeviceSrc.height()};
|
||||
NppiPoint oSrcOffset = {0, 0};
|
||||
|
||||
// create struct with ROI size
|
||||
NppiSize oSizeROI = {(int)oDeviceSrc.width(), (int)oDeviceSrc.height()};
|
||||
// allocate device image of appropriately reduced size
|
||||
npp::ImageNPP_8u_C1 oDeviceDst(oSizeROI.width, oSizeROI.height);
|
||||
// set anchor point inside the mask to (oMaskSize.width / 2,
|
||||
// oMaskSize.height / 2) It should round down when odd
|
||||
NppiPoint oAnchor = {oMaskSize.width / 2, oMaskSize.height / 2};
|
||||
|
||||
// run box filter
|
||||
NPP_CHECK_NPP(nppiFilterBoxBorder_8u_C1R_Ctx(oDeviceSrc.data(),
|
||||
oDeviceSrc.pitch(),
|
||||
oSrcSize,
|
||||
oSrcOffset,
|
||||
oDeviceDst.data(),
|
||||
oDeviceDst.pitch(),
|
||||
oSizeROI,
|
||||
oMaskSize,
|
||||
oAnchor,
|
||||
NPP_BORDER_REPLICATE,
|
||||
nppStreamCtx));
|
||||
|
||||
// declare a host image for the result
|
||||
npp::ImageCPU_8u_C1 oHostDst(oDeviceDst.size());
|
||||
// and copy the device result data into it
|
||||
oDeviceDst.copyTo(oHostDst.data(), oHostDst.pitch());
|
||||
|
||||
saveImage(sResultFilename, oHostDst);
|
||||
std::cout << "Saved image: " << sResultFilename << std::endl;
|
||||
|
||||
nppiFree(oDeviceSrc.data());
|
||||
nppiFree(oDeviceDst.data());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
catch (npp::Exception &rException) {
|
||||
std::cerr << "Program error! The following exception occurred: \n";
|
||||
std::cerr << rException << std::endl;
|
||||
std::cerr << "Aborting." << std::endl;
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Program error! An unknow type of exception occurred. \n";
|
||||
std::cerr << "Aborting." << std::endl;
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
cpp/4_CUDA_Libraries/boxFilterNPP/teapot512.pgm
Normal file
BIN
cpp/4_CUDA_Libraries/boxFilterNPP/teapot512.pgm
Normal file
Binary file not shown.
18
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
71
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/CMakeLists.txt
Normal file
71
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/CMakeLists.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cannyEdgeDetectorNPP LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(
|
||||
../../../Common
|
||||
../../../Common/UtilNPP
|
||||
)
|
||||
|
||||
# Source file
|
||||
find_package(FreeImage)
|
||||
|
||||
if(${FreeImage_FOUND})
|
||||
# Add target for cannyEdgeDetectorNPP
|
||||
add_executable(cannyEdgeDetectorNPP cannyEdgeDetectorNPP.cpp)
|
||||
|
||||
target_compile_options(cannyEdgeDetectorNPP PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cannyEdgeDetectorNPP PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cannyEdgeDetectorNPP PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(cannyEdgeDetectorNPP PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${FreeImage_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(cannyEdgeDetectorNPP PRIVATE
|
||||
CUDA::nppc
|
||||
CUDA::nppisu
|
||||
CUDA::nppif
|
||||
CUDA::cudart
|
||||
${FreeImage_LIBRARIES}
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cannyEdgeDetectorNPP POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/teapot512.pgm
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET cannyEdgeDetectorNPP
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${FreeImage_LIBRARY}/../FreeImage.dll
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "FreeImage not found - will not build sample 'cannyEdgeDetectorNPP'")
|
||||
endif()
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
36
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/README.md
Normal file
36
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# cannyEdgeDetectorNPP - Canny Edge Detector NPP
|
||||
|
||||
## Description
|
||||
|
||||
An NPP CUDA Sample that demonstrates the recommended parameters to use with the nppiFilterCannyBorder_8u_C1R Canny Edge Detection image filter function. This function expects a single channel 8-bit grayscale input image. You can generate a grayscale image from a color image by first calling nppiColorToGray() or nppiRGBToGray(). The Canny Edge Detection function combines and improves on the techniques required to produce an edge detection image using multiple steps.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Performance Strategies, Image Processing, NPP Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaRuntimeGetVersion, cudaFree, cudaSetDevice, cudaGetDeviceCount, cudaDeviceInit, cudaDriverGetVersion, cudaMalloc, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[FreeImage](../../../README.md#freeimage), [NPP](../../../README.md#npp)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,260 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#pragma warning(disable : 4819)
|
||||
#endif
|
||||
|
||||
#include <Exceptions.h>
|
||||
#include <ImageIO.h>
|
||||
#include <ImagesCPU.h>
|
||||
#include <ImagesNPP.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <fstream>
|
||||
#include <helper_cuda.h>
|
||||
#include <helper_string.h>
|
||||
#include <iostream>
|
||||
#include <npp.h>
|
||||
#include <string.h>
|
||||
|
||||
inline int cudaDeviceInit(int argc, const char **argv)
|
||||
{
|
||||
int deviceCount;
|
||||
checkCudaErrors(cudaGetDeviceCount(&deviceCount));
|
||||
|
||||
if (deviceCount == 0) {
|
||||
std::cerr << "CUDA error: no devices supporting CUDA." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int dev = findCudaDevice(argc, argv);
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, dev);
|
||||
std::cerr << "cudaSetDevice GPU" << dev << " = " << deviceProp.name << std::endl;
|
||||
|
||||
checkCudaErrors(cudaSetDevice(dev));
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s Starting...\n\n", argv[0]);
|
||||
|
||||
try {
|
||||
std::string sFilename;
|
||||
char *filePath;
|
||||
|
||||
cudaDeviceInit(argc, (const char **)argv);
|
||||
|
||||
NppStreamContext nppStreamCtx;
|
||||
nppStreamCtx.hStream =
|
||||
0; // The NULL stream by default, set this to whatever your stream ID is if not the NULL stream.
|
||||
|
||||
cudaError_t cudaError = cudaGetDevice(&nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess) {
|
||||
printf("CUDA error: no devices supporting CUDA.\n");
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
}
|
||||
|
||||
const NppLibraryVersion *libVer = nppGetLibVersion();
|
||||
|
||||
printf("NPP Library Version %d.%d.%d\n", libVer->major, libVer->minor, libVer->build);
|
||||
|
||||
int driverVersion, runtimeVersion;
|
||||
cudaDriverGetVersion(&driverVersion);
|
||||
cudaRuntimeGetVersion(&runtimeVersion);
|
||||
|
||||
printf("CUDA Driver Version: %d.%d\n", driverVersion / 1000, (driverVersion % 100) / 10);
|
||||
printf("CUDA Runtime Version: %d.%d\n\n", runtimeVersion / 1000, (runtimeVersion % 100) / 10);
|
||||
|
||||
cudaError = cudaDeviceGetAttribute(&nppStreamCtx.nCudaDevAttrComputeCapabilityMajor,
|
||||
cudaDevAttrComputeCapabilityMajor,
|
||||
nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess)
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
|
||||
cudaError = cudaDeviceGetAttribute(&nppStreamCtx.nCudaDevAttrComputeCapabilityMinor,
|
||||
cudaDevAttrComputeCapabilityMinor,
|
||||
nppStreamCtx.nCudaDeviceId);
|
||||
if (cudaError != cudaSuccess)
|
||||
return NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY;
|
||||
|
||||
cudaError = cudaStreamGetFlags(nppStreamCtx.hStream, &nppStreamCtx.nStreamFlags);
|
||||
|
||||
cudaDeviceProp oDeviceProperties;
|
||||
|
||||
cudaError = cudaGetDeviceProperties(&oDeviceProperties, nppStreamCtx.nCudaDeviceId);
|
||||
|
||||
nppStreamCtx.nMultiProcessorCount = oDeviceProperties.multiProcessorCount;
|
||||
nppStreamCtx.nMaxThreadsPerMultiProcessor = oDeviceProperties.maxThreadsPerMultiProcessor;
|
||||
nppStreamCtx.nMaxThreadsPerBlock = oDeviceProperties.maxThreadsPerBlock;
|
||||
nppStreamCtx.nSharedMemPerBlock = oDeviceProperties.sharedMemPerBlock;
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "input")) {
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "input", &filePath);
|
||||
}
|
||||
else {
|
||||
filePath = sdkFindFilePath("teapot512.pgm", argv[0]);
|
||||
}
|
||||
|
||||
if (filePath) {
|
||||
sFilename = filePath;
|
||||
}
|
||||
else {
|
||||
sFilename = "teapot512.pgm";
|
||||
}
|
||||
|
||||
// if we specify the filename at the command line, then we only test
|
||||
// sFilename[0].
|
||||
int file_errors = 0;
|
||||
std::ifstream infile(sFilename.data(), std::ifstream::in);
|
||||
|
||||
if (infile.good()) {
|
||||
std::cout << "cannyEdgeDetectionNPP opened: <" << sFilename.data() << "> successfully!" << std::endl;
|
||||
file_errors = 0;
|
||||
infile.close();
|
||||
}
|
||||
else {
|
||||
std::cout << "cannyEdgeDetectionNPP unable to open: <" << sFilename.data() << ">" << std::endl;
|
||||
file_errors++;
|
||||
infile.close();
|
||||
}
|
||||
|
||||
if (file_errors > 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string sResultFilename = sFilename;
|
||||
|
||||
std::string::size_type dot = sResultFilename.rfind('.');
|
||||
|
||||
if (dot != std::string::npos) {
|
||||
sResultFilename = sResultFilename.substr(0, dot);
|
||||
}
|
||||
|
||||
sResultFilename += "_cannyEdgeDetection.pgm";
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "output")) {
|
||||
char *outputFilePath;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "output", &outputFilePath);
|
||||
sResultFilename = outputFilePath;
|
||||
}
|
||||
|
||||
// declare a host image object for an 8-bit grayscale image
|
||||
npp::ImageCPU_8u_C1 oHostSrc;
|
||||
// load gray-scale image from disk
|
||||
npp::loadImage(sFilename, oHostSrc);
|
||||
// declare a device image and copy construct from the host image,
|
||||
// i.e. upload host to device
|
||||
npp::ImageNPP_8u_C1 oDeviceSrc(oHostSrc);
|
||||
|
||||
NppiSize oSrcSize = {(int)oDeviceSrc.width(), (int)oDeviceSrc.height()};
|
||||
NppiPoint oSrcOffset = {0, 0};
|
||||
|
||||
// create struct with ROI size
|
||||
NppiSize oSizeROI = {(int)oDeviceSrc.width(), (int)oDeviceSrc.height()};
|
||||
// allocate device image of appropriately reduced size
|
||||
npp::ImageNPP_8u_C1 oDeviceDst(oSizeROI.width, oSizeROI.height);
|
||||
|
||||
int nBufferSize = 0;
|
||||
Npp8u *pScratchBufferNPP = 0;
|
||||
|
||||
// get necessary scratch buffer size and allocate that much device memory
|
||||
NPP_CHECK_NPP(nppiFilterCannyBorderGetBufferSize(oSizeROI, &nBufferSize));
|
||||
|
||||
cudaMalloc((void **)&pScratchBufferNPP, nBufferSize);
|
||||
|
||||
// now run the canny edge detection filter
|
||||
// Using nppiNormL2 will produce larger magnitude values allowing for finer
|
||||
// control of threshold values while nppiNormL1 will be slightly faster.
|
||||
// Also, selecting the sobel gradient filter allows up to a 5x5 kernel size
|
||||
// which can produce more precise results but is a bit slower. Commonly
|
||||
// nppiNormL2 and sobel gradient filter size of 3x3 are used. Canny
|
||||
// recommends that the high threshold value should be about 3 times the low
|
||||
// threshold value. The threshold range will depend on the range of
|
||||
// magnitude values that the sobel gradient filter generates for a
|
||||
// particular image.
|
||||
|
||||
Npp16s nLowThreshold = 72;
|
||||
Npp16s nHighThreshold = 256;
|
||||
|
||||
if ((nBufferSize > 0) && (pScratchBufferNPP != 0)) {
|
||||
NPP_CHECK_NPP(nppiFilterCannyBorder_8u_C1R_Ctx(oDeviceSrc.data(),
|
||||
oDeviceSrc.pitch(),
|
||||
oSrcSize,
|
||||
oSrcOffset,
|
||||
oDeviceDst.data(),
|
||||
oDeviceDst.pitch(),
|
||||
oSizeROI,
|
||||
NPP_FILTER_SOBEL,
|
||||
NPP_MASK_SIZE_3_X_3,
|
||||
nLowThreshold,
|
||||
nHighThreshold,
|
||||
nppiNormL2,
|
||||
NPP_BORDER_REPLICATE,
|
||||
pScratchBufferNPP,
|
||||
nppStreamCtx));
|
||||
}
|
||||
|
||||
// free scratch buffer memory
|
||||
cudaFree(pScratchBufferNPP);
|
||||
|
||||
// declare a host image for the result
|
||||
npp::ImageCPU_8u_C1 oHostDst(oDeviceDst.size());
|
||||
// and copy the device result data into it
|
||||
oDeviceDst.copyTo(oHostDst.data(), oHostDst.pitch());
|
||||
|
||||
saveImage(sResultFilename, oHostDst);
|
||||
std::cout << "Saved image: " << sResultFilename << std::endl;
|
||||
|
||||
nppiFree(oDeviceSrc.data());
|
||||
nppiFree(oDeviceDst.data());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
catch (npp::Exception &rException) {
|
||||
std::cerr << "Program error! The following exception occurred: \n";
|
||||
std::cerr << rException << std::endl;
|
||||
std::cerr << "Aborting." << std::endl;
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Program error! An unknow type of exception occurred. \n";
|
||||
std::cerr << "Aborting." << std::endl;
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/teapot512.pgm
Normal file
BIN
cpp/4_CUDA_Libraries/cannyEdgeDetectorNPP/teapot512.pgm
Normal file
Binary file not shown.
18
cpp/4_CUDA_Libraries/conjugateGradient/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/conjugateGradient/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/conjugateGradient/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/conjugateGradient/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
44
cpp/4_CUDA_Libraries/conjugateGradient/CMakeLists.txt
Normal file
44
cpp/4_CUDA_Libraries/conjugateGradient/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(conjugateGradient LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for conjugateGradient
|
||||
add_executable(conjugateGradient main.cpp)
|
||||
|
||||
target_compile_options(conjugateGradient PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(conjugateGradient PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(conjugateGradient PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(conjugateGradient PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(conjugateGradient PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusparse
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
36
cpp/4_CUDA_Libraries/conjugateGradient/README.md
Normal file
36
cpp/4_CUDA_Libraries/conjugateGradient/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# conjugateGradient - ConjugateGradient
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a conjugate gradient solver on GPU using CUBLAS and CUSPARSE library.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUBLAS Library, CUSPARSE Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaFree, cudaDeviceSynchronize, cudaMalloc, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUBLAS](../../../README.md#cublas), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
302
cpp/4_CUDA_Libraries/conjugateGradient/main.cpp
Normal file
302
cpp/4_CUDA_Libraries/conjugateGradient/main.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/* 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 GPU
|
||||
* using CUBLAS and CUSPARSE
|
||||
*
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Using updated (v2) interfaces to cublas */
|
||||
#include <cublas_v2.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <cusparse.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <helper_cuda.h> // helper function CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper for shared functions common to CUDA Samples
|
||||
|
||||
const char *sSDKname = "conjugateGradient";
|
||||
|
||||
/* genTridiag: generate a random tridiagonal symmetric matrix */
|
||||
void genTridiag(int *I, int *J, 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;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int M = 0, N = 0, nz = 0, *I = NULL, *J = NULL;
|
||||
float *val = NULL;
|
||||
const float tol = 1e-5f;
|
||||
const int max_iter = 10000;
|
||||
float *x;
|
||||
float *rhs;
|
||||
float a, b, na, r0, r1;
|
||||
int *d_col, *d_row;
|
||||
float *d_val, *d_x, dot;
|
||||
float *d_r, *d_p, *d_Ax;
|
||||
int k;
|
||||
float alpha, beta, alpham1;
|
||||
|
||||
// This will pick the best possible CUDA capable device
|
||||
cudaDeviceProp deviceProp;
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (devID < 0) {
|
||||
printf("exiting...\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
// Statistics about the GPU device
|
||||
printf("> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\n\n",
|
||||
deviceProp.multiProcessorCount,
|
||||
deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
/* Generate a random tridiagonal symmetric matrix in CSR format */
|
||||
M = N = 1048576;
|
||||
nz = (N - 2) * 3 + 4;
|
||||
I = (int *)malloc(sizeof(int) * (N + 1));
|
||||
J = (int *)malloc(sizeof(int) * nz);
|
||||
val = (float *)malloc(sizeof(float) * nz);
|
||||
genTridiag(I, J, val, N, nz);
|
||||
|
||||
x = (float *)malloc(sizeof(float) * N);
|
||||
rhs = (float *)malloc(sizeof(float) * N);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
rhs[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
|
||||
/* Get handle to the CUBLAS context */
|
||||
cublasHandle_t cublasHandle = 0;
|
||||
cublasStatus_t cublasStatus;
|
||||
cublasStatus = cublasCreate(&cublasHandle);
|
||||
|
||||
checkCudaErrors(cublasStatus);
|
||||
|
||||
/* Get handle to the CUSPARSE context */
|
||||
cusparseHandle_t cusparseHandle = 0;
|
||||
checkCudaErrors(cusparseCreate(&cusparseHandle));
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&d_col, nz * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_row, (N + 1) * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_val, nz * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_p, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_Ax, N * sizeof(float)));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
N,
|
||||
N,
|
||||
nz,
|
||||
d_row,
|
||||
d_col,
|
||||
d_val,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecx, N, d_x, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecp = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecp, N, d_p, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecAx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecAx, N, d_Ax, CUDA_R_32F));
|
||||
|
||||
/* Initialize problem data */
|
||||
cudaMemcpy(d_col, J, nz * sizeof(int), cudaMemcpyHostToDevice);
|
||||
cudaMemcpy(d_row, I, (N + 1) * sizeof(int), cudaMemcpyHostToDevice);
|
||||
cudaMemcpy(d_val, val, nz * sizeof(float), cudaMemcpyHostToDevice);
|
||||
cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice);
|
||||
cudaMemcpy(d_r, rhs, N * sizeof(float), cudaMemcpyHostToDevice);
|
||||
|
||||
alpha = 1.0;
|
||||
alpham1 = -1.0;
|
||||
beta = 0.0;
|
||||
r0 = 0.;
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
size_t bufferSize = 0;
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecx,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSize));
|
||||
void *buffer = NULL;
|
||||
checkCudaErrors(cudaMalloc(&buffer, bufferSize));
|
||||
|
||||
/* Begin CG */
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecx,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
cublasSaxpy(cublasHandle, N, &alpham1, d_Ax, 1, d_r, 1);
|
||||
cublasStatus = cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, &r1);
|
||||
|
||||
k = 1;
|
||||
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
if (k > 1) {
|
||||
b = r1 / r0;
|
||||
cublasStatus = cublasSscal(cublasHandle, N, &b, d_p, 1);
|
||||
cublasStatus = cublasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1);
|
||||
}
|
||||
else {
|
||||
cublasStatus = cublasScopy(cublasHandle, N, d_r, 1, d_p, 1);
|
||||
}
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecp,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
cublasStatus = cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, &dot);
|
||||
a = r1 / dot;
|
||||
|
||||
cublasStatus = cublasSaxpy(cublasHandle, N, &a, d_p, 1, d_x, 1);
|
||||
na = -a;
|
||||
cublasStatus = cublasSaxpy(cublasHandle, N, &na, d_Ax, 1, d_r, 1);
|
||||
|
||||
r0 = r1;
|
||||
cublasStatus = cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, &r1);
|
||||
cudaDeviceSynchronize();
|
||||
printf("iteration = %3d, residual = %e\n", k, sqrt(r1));
|
||||
k++;
|
||||
}
|
||||
|
||||
cudaMemcpy(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost);
|
||||
|
||||
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[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs[i]);
|
||||
|
||||
if (diff > err) {
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
checkCudaErrors(cudaFree(buffer));
|
||||
}
|
||||
|
||||
cusparseDestroy(cusparseHandle);
|
||||
cublasDestroy(cublasHandle);
|
||||
if (matA) {
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
}
|
||||
if (vecx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecx));
|
||||
}
|
||||
if (vecAx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecAx));
|
||||
}
|
||||
if (vecp) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecp));
|
||||
}
|
||||
|
||||
free(I);
|
||||
free(J);
|
||||
free(val);
|
||||
free(x);
|
||||
free(rhs);
|
||||
cudaFree(d_col);
|
||||
cudaFree(d_row);
|
||||
cudaFree(d_val);
|
||||
cudaFree(d_x);
|
||||
cudaFree(d_r);
|
||||
cudaFree(d_p);
|
||||
cudaFree(d_Ax);
|
||||
|
||||
printf("Test Summary: Error amount = %f\n", err);
|
||||
exit((k <= max_iter) ? 0 : 1);
|
||||
}
|
||||
18
cpp/4_CUDA_Libraries/conjugateGradientCudaGraphs/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/conjugateGradientCudaGraphs/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/conjugateGradientCudaGraphs/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/conjugateGradientCudaGraphs/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(conjugateGradientCudaGraphs LANGUAGES CUDA CXX)
|
||||
|
||||
# Disable response file for libraries on QNX as qcc does not support lib paths with double quotes
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)
|
||||
endif()
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for conjugateGradientCudaGraphs
|
||||
add_executable(conjugateGradientCudaGraphs conjugateGradientCudaGraphs.cu)
|
||||
|
||||
target_compile_options(conjugateGradientCudaGraphs PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(conjugateGradientCudaGraphs PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(conjugateGradientCudaGraphs PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(conjugateGradientCudaGraphs PRIVATE
|
||||
CUDA::cublas
|
||||
CUDA::cusparse
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
36
cpp/4_CUDA_Libraries/conjugateGradientCudaGraphs/README.md
Normal file
36
cpp/4_CUDA_Libraries/conjugateGradientCudaGraphs/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# conjugateGradientCudaGraphs - Conjugate Gradient using Cuda Graphs
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a conjugate gradient solver on GPU using CUBLAS and CUSPARSE library calls captured and called using CUDA Graph APIs.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUBLAS Library, CUSPARSE Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaGraphInstantiate, cudaStreamDestroy, cudaStreamBeginCapture, cudaFree, cudaMallocHost, cudaStreamEndCapture, cudaGraphDestroy, cudaFreeHost, cudaGraphLaunch, cudaStreamCreate, cudaStreamSynchronize, cudaOccupancyMaxPotentialBlockSize, cudaMalloc, cudaMemcpyAsync, cudaMemsetAsync, cudaGetDeviceProperties, cudaGraphExecDestroy
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUBLAS](../../../README.md#cublas), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,460 @@
|
||||
/* 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 GPU
|
||||
* using CUBLAS and CUSPARSE with CUDA Graphs
|
||||
*
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Using updated (v2) interfaces to cublas */
|
||||
#include <cublas_v2.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <cusparse.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <helper_cuda.h> // helper function CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper for shared functions common to CUDA Samples
|
||||
|
||||
const char *sSDKname = "conjugateGradientCudaGraphs";
|
||||
|
||||
#ifndef WITH_GRAPH
|
||||
#define WITH_GRAPH 1
|
||||
#endif
|
||||
|
||||
/* genTridiag: generate a random tridiagonal symmetric matrix */
|
||||
void genTridiag(int *I, int *J, 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;
|
||||
}
|
||||
|
||||
__global__ void initVectors(float *rhs, float *x, int N)
|
||||
{
|
||||
size_t gid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (size_t i = gid; i < N; i += gridDim.x * blockDim.x) {
|
||||
rhs[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void r1_div_x(float *r1, float *r0, float *b)
|
||||
{
|
||||
int gid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (gid == 0) {
|
||||
b[0] = r1[0] / r0[0];
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void a_minus(float *a, float *na)
|
||||
{
|
||||
int gid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (gid == 0) {
|
||||
na[0] = -(a[0]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int N = 0, nz = 0, *I = NULL, *J = NULL;
|
||||
float *val = NULL;
|
||||
const float tol = 1e-5f;
|
||||
const int max_iter = 10000;
|
||||
float *x;
|
||||
float *rhs;
|
||||
float r1;
|
||||
|
||||
int *d_col, *d_row;
|
||||
float *d_val, *d_x;
|
||||
float *d_r, *d_p, *d_Ax;
|
||||
int k;
|
||||
float alpha, beta, alpham1;
|
||||
|
||||
cudaStream_t stream1, streamForGraph;
|
||||
|
||||
// This will pick the best possible CUDA capable device
|
||||
cudaDeviceProp deviceProp;
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (devID < 0) {
|
||||
printf("exiting...\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
// Statistics about the GPU device
|
||||
printf("> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\n\n",
|
||||
deviceProp.multiProcessorCount,
|
||||
deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
/* Generate a random tridiagonal symmetric matrix in CSR format */
|
||||
N = 1048576;
|
||||
nz = (N - 2) * 3 + 4;
|
||||
checkCudaErrors(cudaMallocHost(&I, sizeof(int) * (N + 1)));
|
||||
checkCudaErrors(cudaMallocHost(&J, sizeof(int) * nz));
|
||||
checkCudaErrors(cudaMallocHost(&val, sizeof(float) * nz));
|
||||
genTridiag(I, J, val, N, nz);
|
||||
|
||||
checkCudaErrors(cudaMallocHost(&x, sizeof(float) * N));
|
||||
rhs = (float *)malloc(sizeof(float) * N);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
rhs[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
|
||||
/* Get handle to the CUBLAS context */
|
||||
cublasHandle_t cublasHandle = 0;
|
||||
cublasStatus_t cublasStatus;
|
||||
cublasStatus = cublasCreate(&cublasHandle);
|
||||
|
||||
checkCudaErrors(cublasStatus);
|
||||
|
||||
/* Get handle to the CUSPARSE context */
|
||||
cusparseHandle_t cusparseHandle = 0;
|
||||
cusparseStatus_t cusparseStatus;
|
||||
cusparseStatus = cusparseCreate(&cusparseHandle);
|
||||
|
||||
checkCudaErrors(cusparseStatus);
|
||||
|
||||
checkCudaErrors(cudaStreamCreate(&stream1));
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&d_col, nz * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_row, (N + 1) * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_val, nz * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_p, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_Ax, N * sizeof(float)));
|
||||
|
||||
float *d_r1, *d_r0, *d_dot, *d_a, *d_na, *d_b;
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r1, sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r0, sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_dot, sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_a, sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_na, sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_b, sizeof(float)));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
N,
|
||||
N,
|
||||
nz,
|
||||
d_row,
|
||||
d_col,
|
||||
d_val,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecx, N, d_x, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecp = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecp, N, d_p, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecAx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecAx, N, d_Ax, CUDA_R_32F));
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
size_t bufferSize = 0;
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecx,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSize));
|
||||
void *buffer = NULL;
|
||||
checkCudaErrors(cudaMalloc(&buffer, bufferSize));
|
||||
|
||||
cusparseMatDescr_t descr = 0;
|
||||
checkCudaErrors(cusparseCreateMatDescr(&descr));
|
||||
|
||||
checkCudaErrors(cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL));
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO));
|
||||
|
||||
int numBlocks = 0, blockSize = 0;
|
||||
checkCudaErrors(cudaOccupancyMaxPotentialBlockSize(&numBlocks, &blockSize, initVectors));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(d_col, J, nz * sizeof(int), cudaMemcpyHostToDevice, stream1));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_row, I, (N + 1) * sizeof(int), cudaMemcpyHostToDevice, stream1));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_val, val, nz * sizeof(float), cudaMemcpyHostToDevice, stream1));
|
||||
|
||||
initVectors<<<numBlocks, blockSize, 0, stream1>>>(d_r, d_x, N);
|
||||
|
||||
alpha = 1.0;
|
||||
alpham1 = -1.0;
|
||||
beta = 0.0;
|
||||
|
||||
checkCudaErrors(cusparseSetStream(cusparseHandle, stream1));
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecx,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cublasSetStream(cublasHandle, stream1));
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpham1, d_Ax, 1, d_r, 1));
|
||||
|
||||
checkCudaErrors(cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));
|
||||
|
||||
k = 1;
|
||||
// First Iteration when k=1 starts
|
||||
checkCudaErrors(cublasScopy(cublasHandle, N, d_r, 1, d_p, 1));
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecp,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));
|
||||
|
||||
r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);
|
||||
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));
|
||||
|
||||
a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);
|
||||
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(d_r0, d_r1, sizeof(float), cudaMemcpyDeviceToDevice, stream1));
|
||||
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(&r1, d_r1, sizeof(float), cudaMemcpyDeviceToHost, stream1));
|
||||
checkCudaErrors(cudaStreamSynchronize(stream1));
|
||||
printf("iteration = %3d, residual = %e\n", k, sqrt(r1));
|
||||
// First Iteration when k=1 ends
|
||||
k++;
|
||||
|
||||
#if WITH_GRAPH
|
||||
cudaGraph_t initGraph;
|
||||
checkCudaErrors(cudaStreamCreate(&streamForGraph));
|
||||
checkCudaErrors(cublasSetStream(cublasHandle, stream1));
|
||||
checkCudaErrors(cusparseSetStream(cusparseHandle, stream1));
|
||||
checkCudaErrors(cudaStreamBeginCapture(stream1, cudaStreamCaptureModeGlobal));
|
||||
|
||||
r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_r0, d_b);
|
||||
cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);
|
||||
checkCudaErrors(cublasSscal(cublasHandle, N, d_b, d_p, 1));
|
||||
cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_HOST);
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1));
|
||||
cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);
|
||||
|
||||
checkCudaErrors(cusparseSetPointerMode(cusparseHandle, CUSPARSE_POINTER_MODE_HOST));
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecp,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemsetAsync(d_dot, 0, sizeof(float), stream1));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));
|
||||
|
||||
r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);
|
||||
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));
|
||||
|
||||
a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);
|
||||
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(d_r0, d_r1, sizeof(float), cudaMemcpyDeviceToDevice, stream1));
|
||||
checkCudaErrors(cudaMemsetAsync(d_r1, 0, sizeof(float), stream1));
|
||||
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync((float *)&r1, d_r1, sizeof(float), cudaMemcpyDeviceToHost, stream1));
|
||||
|
||||
checkCudaErrors(cudaStreamEndCapture(stream1, &initGraph));
|
||||
cudaGraphExec_t graphExec;
|
||||
checkCudaErrors(cudaGraphInstantiate(&graphExec, initGraph, NULL, NULL, 0));
|
||||
#endif
|
||||
|
||||
checkCudaErrors(cublasSetStream(cublasHandle, stream1));
|
||||
checkCudaErrors(cusparseSetStream(cusparseHandle, stream1));
|
||||
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
#if WITH_GRAPH
|
||||
checkCudaErrors(cudaGraphLaunch(graphExec, streamForGraph));
|
||||
checkCudaErrors(cudaStreamSynchronize(streamForGraph));
|
||||
#else
|
||||
r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_r0, d_b);
|
||||
cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);
|
||||
checkCudaErrors(cublasSscal(cublasHandle, N, d_b, d_p, 1));
|
||||
|
||||
cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_HOST);
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecp,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));
|
||||
|
||||
r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);
|
||||
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));
|
||||
|
||||
a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(d_r0, d_r1, sizeof(float), cudaMemcpyDeviceToDevice, stream1));
|
||||
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));
|
||||
checkCudaErrors(cudaMemcpyAsync((float *)&r1, d_r1, sizeof(float), cudaMemcpyDeviceToHost, stream1));
|
||||
checkCudaErrors(cudaStreamSynchronize(stream1));
|
||||
#endif
|
||||
printf("iteration = %3d, residual = %e\n", k, sqrt(r1));
|
||||
k++;
|
||||
}
|
||||
|
||||
#if WITH_GRAPH
|
||||
checkCudaErrors(cudaMemcpyAsync(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost, streamForGraph));
|
||||
checkCudaErrors(cudaStreamSynchronize(streamForGraph));
|
||||
#else
|
||||
checkCudaErrors(cudaMemcpyAsync(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost, stream1));
|
||||
checkCudaErrors(cudaStreamSynchronize(stream1));
|
||||
#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[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs[i]);
|
||||
|
||||
if (diff > err) {
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_GRAPH
|
||||
checkCudaErrors(cudaGraphExecDestroy(graphExec));
|
||||
checkCudaErrors(cudaGraphDestroy(initGraph));
|
||||
checkCudaErrors(cudaStreamDestroy(streamForGraph));
|
||||
#endif
|
||||
checkCudaErrors(cudaStreamDestroy(stream1));
|
||||
cusparseDestroy(cusparseHandle);
|
||||
cublasDestroy(cublasHandle);
|
||||
|
||||
if (matA) {
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
}
|
||||
if (vecx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecx));
|
||||
}
|
||||
if (vecAx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecAx));
|
||||
}
|
||||
if (vecp) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecp));
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaFreeHost(I));
|
||||
checkCudaErrors(cudaFreeHost(J));
|
||||
checkCudaErrors(cudaFreeHost(val));
|
||||
checkCudaErrors(cudaFreeHost(x));
|
||||
free(rhs);
|
||||
checkCudaErrors(cudaFree(d_col));
|
||||
checkCudaErrors(cudaFree(d_row));
|
||||
checkCudaErrors(cudaFree(d_val));
|
||||
checkCudaErrors(cudaFree(d_x));
|
||||
checkCudaErrors(cudaFree(d_r));
|
||||
checkCudaErrors(cudaFree(d_p));
|
||||
checkCudaErrors(cudaFree(d_Ax));
|
||||
|
||||
printf("Test Summary: Error amount = %f\n", err);
|
||||
exit((k <= max_iter) ? 0 : 1);
|
||||
}
|
||||
18
cpp/4_CUDA_Libraries/conjugateGradientMultiBlockCG/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/conjugateGradientMultiBlockCG/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/conjugateGradientMultiBlockCG/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/conjugateGradientMultiBlockCG/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(conjugateGradientMultiBlockCG LANGUAGES CUDA CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -maxrregcount=128") # limit register usage to 128 per thread to comply with the maximum number of 32-bit registers per SM
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# This sample is not supported on QNX
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
message(STATUS "Will not build sample ${PROJECT_NAME} - not supported on QNX")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Source file
|
||||
# Add target for conjugateGradientMultiBlockCG
|
||||
add_executable(conjugateGradientMultiBlockCG conjugateGradientMultiBlockCG.cu)
|
||||
|
||||
target_compile_options(conjugateGradientMultiBlockCG PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(conjugateGradientMultiBlockCG PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(conjugateGradientMultiBlockCG PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(conjugateGradientMultiBlockCG PRIVATE
|
||||
CUDA::cublas
|
||||
CUDA::cusparse
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
36
cpp/4_CUDA_Libraries/conjugateGradientMultiBlockCG/README.md
Normal file
36
cpp/4_CUDA_Libraries/conjugateGradientMultiBlockCG/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# conjugateGradientMultiBlockCG - conjugateGradient using MultiBlock Cooperative Groups
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a conjugate gradient solver on GPU using Multi Block Cooperative Groups, also uses Unified Memory.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Unified Memory, Linear Algebra, Cooperative Groups, MultiBlock Cooperative Groups, CUBLAS Library, CUSPARSE Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaFree, cudaMallocManaged, cudaDeviceSynchronize, cudaEventRecord, cudaLaunchCooperativeKernel, cudaEventDestroy, cudaEventElapsedTime, cudaOccupancyMaxActiveBlocksPerMultiprocessor, cudaGetDeviceProperties, cudaEventCreate
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[UVM](../../../README.md#unified-virtual-memory), [MBCG](../../../README.md#multi-block-cooperative-groups)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,527 @@
|
||||
/* 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 GPU using
|
||||
* Multi Block Cooperative Groups, also uses Unified Memory.
|
||||
*
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <cooperative_groups.h>
|
||||
#include <cooperative_groups/reduce.h>
|
||||
#include <helper_cuda.h> // helper function CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper for shared functions common to CUDA Samples
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
const char *sSDKname = "conjugateGradientMultiBlockCG";
|
||||
|
||||
#define ENABLE_CPU_DEBUG_CODE 0
|
||||
#define THREADS_PER_BLOCK 512
|
||||
|
||||
/* genTridiag: generate a random tridiagonal symmetric matrix */
|
||||
void genTridiag(int *I, int *J, float *val, int N, int nz)
|
||||
{
|
||||
I[0] = 0, J[0] = 0, J[1] = 1;
|
||||
val[0] = static_cast<float>(rand()) / RAND_MAX + 10.0f;
|
||||
val[1] = static_cast<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] = static_cast<float>(rand()) / RAND_MAX + 10.0f;
|
||||
|
||||
if (i < N - 1) {
|
||||
val[start + 2] = static_cast<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 nnz, 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;
|
||||
}
|
||||
|
||||
double dotProduct(float *vecA, float *vecB, int size)
|
||||
{
|
||||
double 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++;
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuSpMV(int *I,
|
||||
int *J,
|
||||
float *val,
|
||||
int nnz,
|
||||
int num_rows,
|
||||
float alpha,
|
||||
float *inputVecX,
|
||||
float *outputVecY,
|
||||
cg::thread_block &cta,
|
||||
const cg::grid_group &grid)
|
||||
{
|
||||
for (int i = grid.thread_rank(); i < num_rows; i += grid.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++) {
|
||||
// I or J or val arrays - can be put in shared memory
|
||||
// as the access is random and reused in next calls of gpuSpMV function.
|
||||
output += alpha * val[row_elem + j] * inputVecX[J[row_elem + j]];
|
||||
}
|
||||
|
||||
outputVecY[i] = output;
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuSaxpy(float *x, float *y, float a, int size, const cg::grid_group &grid)
|
||||
{
|
||||
for (int i = grid.thread_rank(); i < size; i += grid.size()) {
|
||||
y[i] = a * x[i] + y[i];
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuDotProduct(float *vecA,
|
||||
float *vecB,
|
||||
double *result,
|
||||
int size,
|
||||
const cg::thread_block &cta,
|
||||
const cg::grid_group &grid)
|
||||
{
|
||||
extern __shared__ double tmp[];
|
||||
|
||||
double temp_sum = 0.0;
|
||||
for (int i = grid.thread_rank(); i < size; i += grid.size()) {
|
||||
temp_sum += static_cast<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;
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
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(result, temp_sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuCopyVector(float *srcA, float *destB, int size, const cg::grid_group &grid)
|
||||
{
|
||||
for (int i = grid.thread_rank(); i < size; i += grid.size()) {
|
||||
destB[i] = srcA[i];
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void
|
||||
gpuScaleVectorAndSaxpy(const float *x, float *y, float a, float scale, int size, const cg::grid_group &grid)
|
||||
{
|
||||
for (int i = grid.thread_rank(); i < size; i += grid.size()) {
|
||||
y[i] = a * x[i] + scale * y[i];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void gpuConjugateGradient(int *I,
|
||||
int *J,
|
||||
float *val,
|
||||
float *x,
|
||||
float *Ax,
|
||||
float *p,
|
||||
float *r,
|
||||
double *dot_result,
|
||||
int nnz,
|
||||
int N,
|
||||
float tol)
|
||||
{
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
cg::grid_group grid = cg::this_grid();
|
||||
|
||||
int max_iter = 10000;
|
||||
|
||||
float alpha = 1.0;
|
||||
float alpham1 = -1.0;
|
||||
float r0 = 0.0, r1, b, a, na;
|
||||
|
||||
gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, cta, grid);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuSaxpy(Ax, r, alpham1, N, grid);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuDotProduct(r, r, dot_result, N, cta, grid);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
r1 = *dot_result;
|
||||
|
||||
int k = 1;
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
if (k > 1) {
|
||||
b = r1 / r0;
|
||||
gpuScaleVectorAndSaxpy(r, p, alpha, b, N, grid);
|
||||
}
|
||||
else {
|
||||
gpuCopyVector(r, p, N, grid);
|
||||
}
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, cta, grid);
|
||||
|
||||
if (threadIdx.x == 0 && blockIdx.x == 0)
|
||||
*dot_result = 0.0;
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuDotProduct(p, Ax, dot_result, N, cta, grid);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
a = r1 / *dot_result;
|
||||
|
||||
gpuSaxpy(p, x, a, N, grid);
|
||||
na = -a;
|
||||
gpuSaxpy(Ax, r, na, N, grid);
|
||||
|
||||
r0 = r1;
|
||||
|
||||
cg::sync(grid);
|
||||
if (threadIdx.x == 0 && blockIdx.x == 0)
|
||||
*dot_result = 0.0;
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuDotProduct(r, r, dot_result, N, cta, grid);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
r1 = *dot_result;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
bool areAlmostEqual(float a, float b, float maxRelDiff)
|
||||
{
|
||||
float diff = fabsf(a - b);
|
||||
float abs_a = fabsf(a);
|
||||
float abs_b = fabsf(b);
|
||||
float largest = abs_a > abs_b ? abs_a : abs_b;
|
||||
|
||||
if (diff <= largest * maxRelDiff) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
printf("maxRelDiff = %.8e\n", maxRelDiff);
|
||||
printf("diff %.8e > largest * maxRelDiff %.8e therefore %.8e and %.8e are not "
|
||||
"same\n",
|
||||
diff,
|
||||
largest * maxRelDiff,
|
||||
a,
|
||||
b);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int N = 0, nz = 0, *I = NULL, *J = NULL;
|
||||
float *val = NULL;
|
||||
const float tol = 1e-5f;
|
||||
float *x;
|
||||
float *rhs;
|
||||
float r1;
|
||||
float *r, *p, *Ax;
|
||||
cudaEvent_t start, stop;
|
||||
|
||||
printf("Starting [%s]...\n", sSDKname);
|
||||
|
||||
// This will pick the best possible CUDA capable device
|
||||
cudaDeviceProp deviceProp;
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
if (!deviceProp.managedMemory) {
|
||||
// This sample requires being run on a device that supports Unified Memory
|
||||
fprintf(stderr, "Unified Memory not supported on this device\n");
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
|
||||
// This sample requires being run on a device that supports Cooperative Kernel
|
||||
// Launch
|
||||
if (!deviceProp.cooperativeLaunch) {
|
||||
printf("\nSelected GPU (%d) does not support Cooperative Kernel Launch, "
|
||||
"Waiving the run\n",
|
||||
devID);
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
|
||||
// Statistics about the GPU device
|
||||
printf("> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\n\n",
|
||||
deviceProp.multiProcessorCount,
|
||||
deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
/* Generate a random tridiagonal symmetric matrix in CSR format */
|
||||
N = 1048576;
|
||||
nz = (N - 2) * 3 + 4;
|
||||
|
||||
cudaMallocManaged(reinterpret_cast<void **>(&I), sizeof(int) * (N + 1));
|
||||
cudaMallocManaged(reinterpret_cast<void **>(&J), sizeof(int) * nz);
|
||||
cudaMallocManaged(reinterpret_cast<void **>(&val), sizeof(float) * nz);
|
||||
|
||||
genTridiag(I, J, val, N, nz);
|
||||
|
||||
cudaMallocManaged(reinterpret_cast<void **>(&x), sizeof(float) * N);
|
||||
cudaMallocManaged(reinterpret_cast<void **>(&rhs), sizeof(float) * N);
|
||||
|
||||
double *dot_result;
|
||||
|
||||
cudaMallocManaged(reinterpret_cast<void **>(&dot_result), sizeof(double));
|
||||
|
||||
*dot_result = 0.0;
|
||||
|
||||
// temp memory for CG
|
||||
checkCudaErrors(cudaMallocManaged(reinterpret_cast<void **>(&r), N * sizeof(float)));
|
||||
checkCudaErrors(cudaMallocManaged(reinterpret_cast<void **>(&p), N * sizeof(float)));
|
||||
checkCudaErrors(cudaMallocManaged(reinterpret_cast<void **>(&Ax), N * sizeof(float)));
|
||||
|
||||
cudaDeviceSynchronize();
|
||||
|
||||
checkCudaErrors(cudaEventCreate(&start));
|
||||
checkCudaErrors(cudaEventCreate(&stop));
|
||||
|
||||
#if ENABLE_CPU_DEBUG_CODE
|
||||
float *Ax_cpu = reinterpret_cast<float *>(malloc(sizeof(float) * N));
|
||||
float *r_cpu = reinterpret_cast<float *>(malloc(sizeof(float) * N));
|
||||
float *p_cpu = reinterpret_cast<float *>(malloc(sizeof(float) * N));
|
||||
float *x_cpu = reinterpret_cast<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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
r[i] = rhs[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
|
||||
void *kernelArgs[] = {
|
||||
(void *)&I,
|
||||
(void *)&J,
|
||||
(void *)&val,
|
||||
(void *)&x,
|
||||
(void *)&Ax,
|
||||
(void *)&p,
|
||||
(void *)&r,
|
||||
(void *)&dot_result,
|
||||
(void *)&nz,
|
||||
(void *)&N,
|
||||
(void *)&tol,
|
||||
};
|
||||
|
||||
int sMemSize = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);
|
||||
int numBlocksPerSm = 0;
|
||||
int numThreads = THREADS_PER_BLOCK;
|
||||
|
||||
checkCudaErrors(
|
||||
cudaOccupancyMaxActiveBlocksPerMultiprocessor(&numBlocksPerSm, gpuConjugateGradient, numThreads, sMemSize));
|
||||
|
||||
int numSms = deviceProp.multiProcessorCount;
|
||||
dim3 dimGrid(numSms * numBlocksPerSm, 1, 1), dimBlock(THREADS_PER_BLOCK, 1, 1);
|
||||
checkCudaErrors(cudaEventRecord(start, 0));
|
||||
checkCudaErrors(
|
||||
cudaLaunchCooperativeKernel((void *)gpuConjugateGradient, dimGrid, dimBlock, kernelArgs, sMemSize, NULL));
|
||||
checkCudaErrors(cudaEventRecord(stop, 0));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
float time;
|
||||
checkCudaErrors(cudaEventElapsedTime(&time, start, stop));
|
||||
|
||||
r1 = *dot_result;
|
||||
|
||||
printf("GPU Final, residual = %e, kernel execution time = %f ms\n", sqrt(r1), time);
|
||||
|
||||
#if ENABLE_CPU_DEBUG_CODE
|
||||
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[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs[i]);
|
||||
|
||||
if (diff > err) {
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaFree(I));
|
||||
checkCudaErrors(cudaFree(J));
|
||||
checkCudaErrors(cudaFree(val));
|
||||
checkCudaErrors(cudaFree(x));
|
||||
checkCudaErrors(cudaFree(rhs));
|
||||
checkCudaErrors(cudaFree(r));
|
||||
checkCudaErrors(cudaFree(p));
|
||||
checkCudaErrors(cudaFree(Ax));
|
||||
checkCudaErrors(cudaFree(dot_result));
|
||||
checkCudaErrors(cudaEventDestroy(start));
|
||||
checkCudaErrors(cudaEventDestroy(stop));
|
||||
|
||||
#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, "&&&& conjugateGradientMultiBlockCG %s\n", (sqrt(r1) < tol) ? "PASSED" : "FAILED");
|
||||
exit((sqrt(r1) < tol) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
18
cpp/4_CUDA_Libraries/conjugateGradientMultiDeviceCG/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/conjugateGradientMultiDeviceCG/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/conjugateGradientMultiDeviceCG/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/conjugateGradientMultiDeviceCG/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(conjugateGradientMultiDeviceCG LANGUAGES CUDA CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# This sample is not supported on QNX
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
message(STATUS "Will not build sample ${PROJECT_NAME} - not supported on QNX")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Source file
|
||||
# Add target for conjugateGradientMultiDeviceCG
|
||||
add_executable(conjugateGradientMultiDeviceCG conjugateGradientMultiDeviceCG.cu)
|
||||
|
||||
target_compile_options(conjugateGradientMultiDeviceCG PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(conjugateGradientMultiDeviceCG PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(conjugateGradientMultiDeviceCG PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_link_libraries(conjugateGradientMultiDeviceCG PRIVATE
|
||||
CUDA::cublas
|
||||
CUDA::cusparse
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
@@ -0,0 +1,36 @@
|
||||
# conjugateGradientMultiDeviceCG - conjugateGradient using MultiDevice Cooperative Groups
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a conjugate gradient solver on multiple GPUs using Multi Device Cooperative Groups, also uses Unified Memory optimized using prefetching and usage hints.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Unified Memory, Linear Algebra, Cooperative Groups, MultiDevice Cooperative Groups, CUBLAS Library, CUSPARSE Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaHostAlloc, cudaMemPrefetchAsync, cudaFree, cudaLaunchCooperativeKernel, cudaMallocManaged, cudaSetDevice, cudaGetDeviceCount, cudaGetDeviceProperties, cudaFreeHost, cudaMemset, cudaStreamCreate, cudaStreamSynchronize, cudaDeviceEnablePeerAccess, cudaMemAdvise, cudaOccupancyMaxActiveBlocksPerMultiprocessor, cudaDeviceCanAccessPeer
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[UVM](../../../README.md#unified-virtual-memory), [MDCG](../../../README.md#multi-device-cooperative-groups), [C++11 CUDA](../../../README.md#c11-cuda)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,812 @@
|
||||
/* 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 <cuda_runtime.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <utility>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <cooperative_groups.h>
|
||||
#include <cooperative_groups/reduce.h>
|
||||
#include <helper_cuda.h> // helper function CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper for shared functions common to CUDA Samples
|
||||
|
||||
namespace cg = cooperative_groups;
|
||||
|
||||
const char *sSDKname = "conjugateGradientMultiDeviceCG";
|
||||
|
||||
#define ENABLE_CPU_DEBUG_CODE 0
|
||||
#define THREADS_PER_BLOCK 512
|
||||
|
||||
__device__ double grid_dot_result = 0.0;
|
||||
|
||||
/* genTridiag: generate a random tridiagonal symmetric matrix */
|
||||
void genTridiag(int *I, int *J, 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 nnz, 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++;
|
||||
}
|
||||
}
|
||||
|
||||
// Data filled on CPU needed for MultiGPU operations.
|
||||
struct MultiDeviceData
|
||||
{
|
||||
unsigned char *hostMemoryArrivedList;
|
||||
unsigned int numDevices;
|
||||
unsigned int deviceRank;
|
||||
};
|
||||
|
||||
// Class used for coordination of multiple devices.
|
||||
class PeerGroup
|
||||
{
|
||||
const MultiDeviceData &data;
|
||||
const cg::grid_group &grid;
|
||||
|
||||
__device__ unsigned char load_arrived(unsigned char *arrived) const
|
||||
{
|
||||
#if __CUDA_ARCH__ < 700
|
||||
return *(volatile unsigned char *)arrived;
|
||||
#else
|
||||
unsigned int result;
|
||||
asm volatile("ld.acquire.sys.global.u8 %0, [%1];" : "=r"(result) : "l"(arrived) : "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
__device__ void store_arrived(unsigned char *arrived, unsigned char val) const
|
||||
{
|
||||
#if __CUDA_ARCH__ < 700
|
||||
*(volatile unsigned char *)arrived = val;
|
||||
#else
|
||||
unsigned int reg_val = val;
|
||||
asm volatile("st.release.sys.global.u8 [%1], %0;" ::"r"(reg_val) "l"(arrived) : "memory");
|
||||
|
||||
// Avoids compiler warnings from unused variable val.
|
||||
(void)(reg_val = reg_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
__device__ PeerGroup(const MultiDeviceData &data, const cg::grid_group &grid)
|
||||
: data(data)
|
||||
, grid(grid) {};
|
||||
|
||||
__device__ unsigned int size() const { return data.numDevices * grid.size(); }
|
||||
|
||||
__device__ unsigned int thread_rank() const { return data.deviceRank * grid.size() + grid.thread_rank(); }
|
||||
|
||||
__device__ void sync() const
|
||||
{
|
||||
grid.sync();
|
||||
|
||||
// One thread from each grid participates in the sync.
|
||||
if (grid.thread_rank() == 0) {
|
||||
if (data.deviceRank == 0) {
|
||||
// Leader grid waits for others to join and then releases them.
|
||||
// Other GPUs can arrive in any order, so the leader have to wait for
|
||||
// all others.
|
||||
for (int i = 0; i < data.numDevices - 1; i++) {
|
||||
while (load_arrived(&data.hostMemoryArrivedList[i]) == 0)
|
||||
;
|
||||
}
|
||||
for (int i = 0; i < data.numDevices - 1; i++) {
|
||||
store_arrived(&data.hostMemoryArrivedList[i], 0);
|
||||
}
|
||||
__threadfence_system();
|
||||
}
|
||||
else {
|
||||
// Other grids note their arrival and wait to be released.
|
||||
store_arrived(&data.hostMemoryArrivedList[data.deviceRank - 1], 1);
|
||||
while (load_arrived(&data.hostMemoryArrivedList[data.deviceRank - 1]) == 1)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
grid.sync();
|
||||
}
|
||||
};
|
||||
|
||||
__device__ void gpuSpMV(int *I,
|
||||
int *J,
|
||||
float *val,
|
||||
int nnz,
|
||||
int num_rows,
|
||||
float alpha,
|
||||
float *inputVecX,
|
||||
float *outputVecY,
|
||||
const PeerGroup &peer_group)
|
||||
{
|
||||
for (int i = peer_group.thread_rank(); i < num_rows; i += peer_group.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;
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuSaxpy(float *x, float *y, float a, int size, const PeerGroup &peer_group)
|
||||
{
|
||||
for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {
|
||||
y[i] = a * x[i] + y[i];
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void
|
||||
gpuDotProduct(float *vecA, float *vecB, int size, const cg::thread_block &cta, const PeerGroup &peer_group)
|
||||
{
|
||||
extern __shared__ double tmp[];
|
||||
|
||||
double temp_sum = 0.0;
|
||||
|
||||
for (int i = peer_group.thread_rank(); i < size; i += peer_group.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;
|
||||
}
|
||||
|
||||
cg::sync(cta);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuCopyVector(float *srcA, float *destB, int size, const PeerGroup &peer_group)
|
||||
{
|
||||
for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {
|
||||
destB[i] = srcA[i];
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void gpuScaleVectorAndSaxpy(float *x, float *y, float a, float scale, int size, const PeerGroup &peer_group)
|
||||
{
|
||||
for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {
|
||||
y[i] = a * x[i] + scale * y[i];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void multiGpuConjugateGradient(int *I,
|
||||
int *J,
|
||||
float *val,
|
||||
float *x,
|
||||
float *Ax,
|
||||
float *p,
|
||||
float *r,
|
||||
double *dot_result,
|
||||
int nnz,
|
||||
int N,
|
||||
float tol,
|
||||
MultiDeviceData multi_device_data)
|
||||
{
|
||||
cg::thread_block cta = cg::this_thread_block();
|
||||
cg::grid_group grid = cg::this_grid();
|
||||
PeerGroup peer_group(multi_device_data, grid);
|
||||
|
||||
const int max_iter = 10000;
|
||||
|
||||
float alpha = 1.0;
|
||||
float alpham1 = -1.0;
|
||||
float r0 = 0.0, r1, b, a, na;
|
||||
|
||||
for (int i = peer_group.thread_rank(); i < N; i += peer_group.size()) {
|
||||
r[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, peer_group);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuSaxpy(Ax, r, alpham1, N, peer_group);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
gpuDotProduct(r, r, N, cta, peer_group);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
if (grid.thread_rank() == 0) {
|
||||
atomicAdd_system(dot_result, grid_dot_result);
|
||||
grid_dot_result = 0.0;
|
||||
}
|
||||
peer_group.sync();
|
||||
|
||||
r1 = *dot_result;
|
||||
|
||||
int k = 1;
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
if (k > 1) {
|
||||
b = r1 / r0;
|
||||
gpuScaleVectorAndSaxpy(r, p, alpha, b, N, peer_group);
|
||||
}
|
||||
else {
|
||||
gpuCopyVector(r, p, N, peer_group);
|
||||
}
|
||||
|
||||
peer_group.sync();
|
||||
|
||||
gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, peer_group);
|
||||
|
||||
if (peer_group.thread_rank() == 0) {
|
||||
*dot_result = 0.0;
|
||||
}
|
||||
peer_group.sync();
|
||||
|
||||
gpuDotProduct(p, Ax, N, cta, peer_group);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
if (grid.thread_rank() == 0) {
|
||||
atomicAdd_system(dot_result, grid_dot_result);
|
||||
grid_dot_result = 0.0;
|
||||
}
|
||||
peer_group.sync();
|
||||
|
||||
a = r1 / *dot_result;
|
||||
|
||||
gpuSaxpy(p, x, a, N, peer_group);
|
||||
|
||||
na = -a;
|
||||
|
||||
gpuSaxpy(Ax, r, na, N, peer_group);
|
||||
|
||||
r0 = r1;
|
||||
|
||||
peer_group.sync();
|
||||
|
||||
if (peer_group.thread_rank() == 0) {
|
||||
*dot_result = 0.0;
|
||||
}
|
||||
|
||||
peer_group.sync();
|
||||
|
||||
gpuDotProduct(r, r, N, cta, peer_group);
|
||||
|
||||
cg::sync(grid);
|
||||
|
||||
if (grid.thread_rank() == 0) {
|
||||
atomicAdd_system(dot_result, grid_dot_result);
|
||||
grid_dot_result = 0.0;
|
||||
}
|
||||
peer_group.sync();
|
||||
|
||||
r1 = *dot_result;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Map of device version to device number
|
||||
std::multimap<std::pair<int, int>, int> getIdenticalGPUs()
|
||||
{
|
||||
int numGpus = 0;
|
||||
checkCudaErrors(cudaGetDeviceCount(&numGpus));
|
||||
|
||||
std::multimap<std::pair<int, int>, int> identicalGpus;
|
||||
|
||||
for (int i = 0; i < numGpus; i++) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));
|
||||
|
||||
// Filter unsupported devices
|
||||
if (deviceProp.cooperativeLaunch && deviceProp.concurrentManagedAccess) {
|
||||
identicalGpus.emplace(std::make_pair(deviceProp.major, deviceProp.minor), i);
|
||||
}
|
||||
printf("GPU Device %d: \"%s\" with compute capability %d.%d\n",
|
||||
i,
|
||||
deviceProp.name,
|
||||
deviceProp.major,
|
||||
deviceProp.minor);
|
||||
}
|
||||
|
||||
return identicalGpus;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
constexpr size_t kNumGpusRequired = 2;
|
||||
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);
|
||||
auto gpusByArch = getIdenticalGPUs();
|
||||
|
||||
auto it = gpusByArch.begin();
|
||||
auto end = gpusByArch.end();
|
||||
|
||||
auto bestFit = std::make_pair(it, it);
|
||||
// use std::distance to find the largest number of GPUs amongst architectures
|
||||
auto distance = [](decltype(bestFit) p) { return std::distance(p.first, p.second); };
|
||||
|
||||
// Read each unique key/pair element in order
|
||||
for (; it != end; it = gpusByArch.upper_bound(it->first)) {
|
||||
// first and second are iterators bounded within the architecture group
|
||||
auto testFit = gpusByArch.equal_range(it->first);
|
||||
// Always use devices with highest architecture version or whichever has the
|
||||
// most devices available
|
||||
if (distance(bestFit) <= distance(testFit))
|
||||
bestFit = testFit;
|
||||
}
|
||||
|
||||
if (distance(bestFit) < kNumGpusRequired) {
|
||||
printf("No two or more GPUs with same architecture capable of "
|
||||
"concurrentManagedAccess found. "
|
||||
"\nWaiving the sample\n");
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
|
||||
std::set<int> bestFitDeviceIds;
|
||||
|
||||
// Check & select peer-to-peer access capable GPU devices as enabling p2p
|
||||
// access between participating GPUs gives better performance.
|
||||
for (auto itr = bestFit.first; itr != bestFit.second; itr++) {
|
||||
int deviceId = itr->second;
|
||||
checkCudaErrors(cudaSetDevice(deviceId));
|
||||
|
||||
std::for_each(itr, bestFit.second, [&deviceId, &bestFitDeviceIds, &kNumGpusRequired](decltype(*itr) mapPair) {
|
||||
if (deviceId != mapPair.second) {
|
||||
int access = 0;
|
||||
checkCudaErrors(cudaDeviceCanAccessPeer(&access, deviceId, mapPair.second));
|
||||
printf("Device=%d %s Access Peer Device=%d\n", deviceId, access ? "CAN" : "CANNOT", mapPair.second);
|
||||
if (access && bestFitDeviceIds.size() < kNumGpusRequired) {
|
||||
bestFitDeviceIds.emplace(deviceId);
|
||||
bestFitDeviceIds.emplace(mapPair.second);
|
||||
}
|
||||
else {
|
||||
printf("Ignoring device %i (max devices exceeded)\n", mapPair.second);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (bestFitDeviceIds.size() >= kNumGpusRequired) {
|
||||
printf("Selected p2p capable devices - ");
|
||||
for (auto devicesItr = bestFitDeviceIds.begin(); devicesItr != bestFitDeviceIds.end(); devicesItr++) {
|
||||
printf("deviceId = %d ", *devicesItr);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if bestFitDeviceIds.size() == 0 it means the GPUs in system are not p2p
|
||||
// capable, hence we add it without p2p capability check.
|
||||
if (!bestFitDeviceIds.size()) {
|
||||
printf("Devices involved are not p2p capable.. selecting %zu of them\n", kNumGpusRequired);
|
||||
std::for_each(
|
||||
bestFit.first, bestFit.second, [&bestFitDeviceIds, &kNumGpusRequired](decltype(*bestFit.first) mapPair) {
|
||||
if (bestFitDeviceIds.size() < kNumGpusRequired) {
|
||||
bestFitDeviceIds.emplace(mapPair.second);
|
||||
}
|
||||
else {
|
||||
printf("Ignoring device %i (max devices exceeded)\n", mapPair.second);
|
||||
}
|
||||
// Insert the sequence into the deviceIds set
|
||||
});
|
||||
}
|
||||
else {
|
||||
// perform cudaDeviceEnablePeerAccess in both directions for all
|
||||
// participating devices.
|
||||
for (auto p1_itr = bestFitDeviceIds.begin(); p1_itr != bestFitDeviceIds.end(); p1_itr++) {
|
||||
checkCudaErrors(cudaSetDevice(*p1_itr));
|
||||
for (auto p2_itr = bestFitDeviceIds.begin(); p2_itr != bestFitDeviceIds.end(); p2_itr++) {
|
||||
if (*p1_itr != *p2_itr) {
|
||||
checkCudaErrors(cudaDeviceEnablePeerAccess(*p2_itr, 0));
|
||||
checkCudaErrors(cudaSetDevice(*p1_itr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a random tridiagonal symmetric matrix in CSR format */
|
||||
N = 10485760 * 2;
|
||||
nz = (N - 2) * 3 + 4;
|
||||
|
||||
checkCudaErrors(cudaMallocManaged((void **)&I, sizeof(int) * (N + 1)));
|
||||
checkCudaErrors(cudaMallocManaged((void **)&J, sizeof(int) * nz));
|
||||
checkCudaErrors(cudaMallocManaged((void **)&val, sizeof(float) * nz));
|
||||
|
||||
float *val_cpu = (float *)malloc(sizeof(float) * nz);
|
||||
|
||||
genTridiag(I, J, val_cpu, N, nz);
|
||||
|
||||
memcpy(val, val_cpu, sizeof(float) * nz);
|
||||
cudaMemLocation deviceLoc;
|
||||
deviceLoc.type = cudaMemLocationTypeDevice;
|
||||
deviceLoc.id = 0; // Device location with initial device 0
|
||||
checkCudaErrors(cudaMemAdvise(I, sizeof(int) * (N + 1), cudaMemAdviseSetReadMostly, deviceLoc));
|
||||
checkCudaErrors(cudaMemAdvise(J, sizeof(int) * nz, cudaMemAdviseSetReadMostly, deviceLoc));
|
||||
checkCudaErrors(cudaMemAdvise(val, sizeof(float) * nz, cudaMemAdviseSetReadMostly, deviceLoc));
|
||||
|
||||
checkCudaErrors(cudaMallocManaged((void **)&x, sizeof(float) * N));
|
||||
|
||||
double *dot_result;
|
||||
checkCudaErrors(cudaMallocManaged((void **)&dot_result, sizeof(double)));
|
||||
|
||||
checkCudaErrors(cudaMemset(dot_result, 0, sizeof(double)));
|
||||
|
||||
// temp memory for ConjugateGradient
|
||||
checkCudaErrors(cudaMallocManaged((void **)&r, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMallocManaged((void **)&p, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMallocManaged((void **)&Ax, N * sizeof(float)));
|
||||
|
||||
std::cout << "\nRunning on GPUs = " << kNumGpusRequired << std::endl;
|
||||
cudaStream_t nStreams[kNumGpusRequired];
|
||||
|
||||
int sMemSize = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);
|
||||
int numBlocksPerSm = INT_MAX;
|
||||
int numThreads = THREADS_PER_BLOCK;
|
||||
int numSms = INT_MAX;
|
||||
auto deviceId = bestFitDeviceIds.begin();
|
||||
|
||||
// set numSms & numBlocksPerSm to be lowest of 2 devices
|
||||
while (deviceId != bestFitDeviceIds.end()) {
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaSetDevice(*deviceId));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, *deviceId));
|
||||
|
||||
int numBlocksPerSm_current = 0;
|
||||
checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(
|
||||
&numBlocksPerSm_current, multiGpuConjugateGradient, numThreads, sMemSize));
|
||||
|
||||
if (numBlocksPerSm > numBlocksPerSm_current) {
|
||||
numBlocksPerSm = numBlocksPerSm_current;
|
||||
}
|
||||
if (numSms > deviceProp.multiProcessorCount) {
|
||||
numSms = deviceProp.multiProcessorCount;
|
||||
}
|
||||
deviceId++;
|
||||
}
|
||||
|
||||
if (!numBlocksPerSm) {
|
||||
printf("Max active blocks per SM is returned as 0.\n Hence, Waiving the "
|
||||
"sample\n");
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
|
||||
int device_count = 0;
|
||||
int totalThreadsPerGPU = numSms * numBlocksPerSm * THREADS_PER_BLOCK;
|
||||
deviceId = bestFitDeviceIds.begin();
|
||||
while (deviceId != bestFitDeviceIds.end()) {
|
||||
checkCudaErrors(cudaSetDevice(*deviceId));
|
||||
checkCudaErrors(cudaStreamCreate(&nStreams[device_count]));
|
||||
|
||||
int perGPUIter = N / (totalThreadsPerGPU * kNumGpusRequired);
|
||||
int offset_Ax = device_count * totalThreadsPerGPU;
|
||||
int offset_r = device_count * totalThreadsPerGPU;
|
||||
int offset_p = device_count * totalThreadsPerGPU;
|
||||
int offset_x = device_count * totalThreadsPerGPU;
|
||||
|
||||
// Create device location with specific device ID
|
||||
cudaMemLocation deviceLoc;
|
||||
deviceLoc.type = cudaMemLocationTypeDevice;
|
||||
deviceLoc.id = *deviceId;
|
||||
checkCudaErrors(cudaMemPrefetchAsync(I, sizeof(int) * N, deviceLoc, 0, nStreams[device_count]));
|
||||
checkCudaErrors(cudaMemPrefetchAsync(val, sizeof(float) * nz, deviceLoc, 0, nStreams[device_count]));
|
||||
checkCudaErrors(cudaMemPrefetchAsync(J, sizeof(float) * nz, deviceLoc, 0, nStreams[device_count]));
|
||||
|
||||
if (offset_Ax <= N) {
|
||||
for (int i = 0; i < perGPUIter; i++) {
|
||||
cudaMemAdvise(
|
||||
Ax + offset_Ax, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);
|
||||
cudaMemAdvise(
|
||||
r + offset_r, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);
|
||||
cudaMemAdvise(
|
||||
x + offset_x, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);
|
||||
cudaMemAdvise(
|
||||
p + offset_p, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);
|
||||
|
||||
cudaMemAdvise(
|
||||
Ax + offset_Ax, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);
|
||||
cudaMemAdvise(r + offset_r, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);
|
||||
cudaMemAdvise(p + offset_p, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);
|
||||
cudaMemAdvise(x + offset_x, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);
|
||||
|
||||
offset_Ax += totalThreadsPerGPU * kNumGpusRequired;
|
||||
offset_r += totalThreadsPerGPU * kNumGpusRequired;
|
||||
offset_p += totalThreadsPerGPU * kNumGpusRequired;
|
||||
offset_x += totalThreadsPerGPU * kNumGpusRequired;
|
||||
|
||||
if (offset_Ax >= N) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_count++;
|
||||
deviceId++;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("Total threads per GPU = %d numBlocksPerSm = %d\n",
|
||||
numSms * numBlocksPerSm * THREADS_PER_BLOCK,
|
||||
numBlocksPerSm);
|
||||
dim3 dimGrid(numSms * numBlocksPerSm, 1, 1), dimBlock(THREADS_PER_BLOCK, 1, 1);
|
||||
|
||||
// Structure used for cross-grid synchronization.
|
||||
MultiDeviceData multi_device_data;
|
||||
checkCudaErrors(cudaHostAlloc(&multi_device_data.hostMemoryArrivedList,
|
||||
(kNumGpusRequired - 1) * sizeof(*multi_device_data.hostMemoryArrivedList),
|
||||
cudaHostAllocPortable));
|
||||
memset(multi_device_data.hostMemoryArrivedList,
|
||||
0,
|
||||
(kNumGpusRequired - 1) * sizeof(*multi_device_data.hostMemoryArrivedList));
|
||||
multi_device_data.numDevices = kNumGpusRequired;
|
||||
multi_device_data.deviceRank = 0;
|
||||
|
||||
void *kernelArgs[] = {
|
||||
(void *)&I,
|
||||
(void *)&J,
|
||||
(void *)&val,
|
||||
(void *)&x,
|
||||
(void *)&Ax,
|
||||
(void *)&p,
|
||||
(void *)&r,
|
||||
(void *)&dot_result,
|
||||
(void *)&nz,
|
||||
(void *)&N,
|
||||
(void *)&tol,
|
||||
(void *)&multi_device_data,
|
||||
};
|
||||
|
||||
printf("Launching kernel\n");
|
||||
|
||||
deviceId = bestFitDeviceIds.begin();
|
||||
device_count = 0;
|
||||
while (deviceId != bestFitDeviceIds.end()) {
|
||||
checkCudaErrors(cudaSetDevice(*deviceId));
|
||||
checkCudaErrors(cudaLaunchCooperativeKernel(
|
||||
(void *)multiGpuConjugateGradient, dimGrid, dimBlock, kernelArgs, sMemSize, nStreams[device_count++]));
|
||||
multi_device_data.deviceRank++;
|
||||
deviceId++;
|
||||
}
|
||||
|
||||
// Synchronize all kernel streams across all devices before prefetching to host.
|
||||
// This ensures GPU writes are fully visible before page migration begins.
|
||||
deviceId = bestFitDeviceIds.begin();
|
||||
device_count = 0;
|
||||
while (deviceId != bestFitDeviceIds.end()) {
|
||||
checkCudaErrors(cudaSetDevice(*deviceId));
|
||||
checkCudaErrors(cudaStreamSynchronize(nStreams[device_count++]));
|
||||
deviceId++;
|
||||
}
|
||||
|
||||
cudaMemLocation hostLoc;
|
||||
hostLoc.type = cudaMemLocationTypeHost;
|
||||
checkCudaErrors(cudaMemPrefetchAsync(x, sizeof(float) * N, hostLoc, 0));
|
||||
checkCudaErrors(cudaMemPrefetchAsync(dot_result, sizeof(double), hostLoc, 0));
|
||||
// Ensure the async prefetch of x/dot_result on the null stream completes before
|
||||
// CPU access. cudaStreamSynchronize only covers the kernel streams, not the null stream.
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
r1 = (float)*dot_result;
|
||||
|
||||
printf("GPU Final, residual = %e \n ", sqrt(r1));
|
||||
|
||||
#if ENABLE_CPU_DEBUG_CODE
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaFreeHost(multi_device_data.hostMemoryArrivedList));
|
||||
checkCudaErrors(cudaFree(I));
|
||||
checkCudaErrors(cudaFree(J));
|
||||
checkCudaErrors(cudaFree(val));
|
||||
checkCudaErrors(cudaFree(x));
|
||||
checkCudaErrors(cudaFree(r));
|
||||
checkCudaErrors(cudaFree(p));
|
||||
checkCudaErrors(cudaFree(Ax));
|
||||
checkCudaErrors(cudaFree(dot_result));
|
||||
free(val_cpu);
|
||||
|
||||
#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);
|
||||
}
|
||||
18
cpp/4_CUDA_Libraries/conjugateGradientPrecond/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/conjugateGradientPrecond/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/conjugateGradientPrecond/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/conjugateGradientPrecond/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
44
cpp/4_CUDA_Libraries/conjugateGradientPrecond/CMakeLists.txt
Normal file
44
cpp/4_CUDA_Libraries/conjugateGradientPrecond/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(conjugateGradientPrecond LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for conjugateGradientPrecond
|
||||
add_executable(conjugateGradientPrecond main.cpp)
|
||||
|
||||
target_compile_options(conjugateGradientPrecond PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(conjugateGradientPrecond PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(conjugateGradientPrecond PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(conjugateGradientPrecond PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(conjugateGradientPrecond PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusparse
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
36
cpp/4_CUDA_Libraries/conjugateGradientPrecond/README.md
Normal file
36
cpp/4_CUDA_Libraries/conjugateGradientPrecond/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# conjugateGradientPrecond - Preconditioned Conjugate Gradient
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a preconditioned conjugate gradient solver on GPU using CUBLAS and CUSPARSE library.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUBLAS Library, CUSPARSE Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaFree, cudaMemset, cudaMalloc, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUBLAS](../../../README.md#cublas), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
626
cpp/4_CUDA_Libraries/conjugateGradientPrecond/main.cpp
Normal file
626
cpp/4_CUDA_Libraries/conjugateGradientPrecond/main.cpp
Normal file
@@ -0,0 +1,626 @@
|
||||
/* 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 preconditioned conjugate gradient solver on
|
||||
* the GPU using CUBLAS and CUSPARSE. Relative to the conjugateGradient
|
||||
* SDK example, this demonstrates the use of cusparseScsrilu02() for
|
||||
* computing the incompute-LU preconditioner and cusparseScsrsv2_solve()
|
||||
* for solving triangular systems. Specifically, the preconditioned
|
||||
* conjugate gradient method with an incomplete LU preconditioner is
|
||||
* used to solve the Laplacian operator in 2D on a uniform mesh.
|
||||
*
|
||||
* Note that the code in this example and the specific matrices used here
|
||||
* were chosen to demonstrate the use of the CUSPARSE library as simply
|
||||
* and as clearly as possible. This is not optimized code and the input
|
||||
* matrices have been chosen for simplicity rather than performance.
|
||||
* These should not be used either as a performance guide or for
|
||||
* benchmarking purposes.
|
||||
*/
|
||||
|
||||
|
||||
// includes, system
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// CUDA Runtime
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
// Using updated (v2) interfaces for CUBLAS and CUSPARSE
|
||||
#include <cublas_v2.h>
|
||||
#include <cusparse.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <helper_cuda.h> // CUDA error checking
|
||||
#include <helper_functions.h> // shared functions common to CUDA Samples
|
||||
|
||||
const char *sSDKname = "conjugateGradientPrecond";
|
||||
|
||||
/*
|
||||
* Generate a matrix representing a second order regular Laplacian operator
|
||||
* on a 2D domain in Compressed Sparse Row format.
|
||||
*/
|
||||
void genLaplace(int *row_ptr, int *col_ind, float *val, int M, int N, int nz, float *rhs)
|
||||
{
|
||||
assert(M == N);
|
||||
int n = (int)sqrt((double)N);
|
||||
assert(n * n == N);
|
||||
printf("laplace dimension = %d\n", n);
|
||||
int idx = 0;
|
||||
|
||||
// loop over degrees of freedom
|
||||
for (int i = 0; i < N; i++) {
|
||||
int ix = i % n;
|
||||
int iy = i / n;
|
||||
|
||||
row_ptr[i] = idx;
|
||||
|
||||
// up
|
||||
if (iy > 0) {
|
||||
val[idx] = 1.0;
|
||||
col_ind[idx] = i - n;
|
||||
idx++;
|
||||
}
|
||||
else {
|
||||
rhs[i] -= 1.0;
|
||||
}
|
||||
|
||||
// left
|
||||
if (ix > 0) {
|
||||
val[idx] = 1.0;
|
||||
col_ind[idx] = i - 1;
|
||||
idx++;
|
||||
}
|
||||
else {
|
||||
rhs[i] -= 0.0;
|
||||
}
|
||||
|
||||
// center
|
||||
val[idx] = -4.0;
|
||||
col_ind[idx] = i;
|
||||
idx++;
|
||||
|
||||
// right
|
||||
if (ix < n - 1) {
|
||||
val[idx] = 1.0;
|
||||
col_ind[idx] = i + 1;
|
||||
idx++;
|
||||
}
|
||||
else {
|
||||
rhs[i] -= 0.0;
|
||||
}
|
||||
|
||||
// down
|
||||
if (iy < n - 1) {
|
||||
val[idx] = 1.0;
|
||||
col_ind[idx] = i + n;
|
||||
idx++;
|
||||
}
|
||||
else {
|
||||
rhs[i] -= 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
row_ptr[N] = idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Solve Ax=b using the conjugate gradient method
|
||||
* a) without any preconditioning,
|
||||
* b) using an Incomplete Cholesky preconditioner, and
|
||||
* c) using an ILU0 preconditioner.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const int max_iter = 1000;
|
||||
int k, M = 0, N = 0, nz = 0, *I = NULL, *J = NULL;
|
||||
int *d_col, *d_row;
|
||||
int qatest = 0;
|
||||
const float tol = 1e-12f;
|
||||
float *x, *rhs;
|
||||
float r0, r1, alpha, beta;
|
||||
float *d_val, *d_x;
|
||||
float *d_zm1, *d_zm2, *d_rm2;
|
||||
float *d_r, *d_p, *d_omega, *d_y;
|
||||
float *val = NULL;
|
||||
float *d_valsILU0;
|
||||
float rsum, diff, err = 0.0;
|
||||
float qaerr1, qaerr2 = 0.0;
|
||||
float dot, numerator, denominator, nalpha;
|
||||
const float floatone = 1.0;
|
||||
const float floatzero = 0.0;
|
||||
|
||||
int nErrors = 0;
|
||||
|
||||
printf("conjugateGradientPrecond starting...\n");
|
||||
|
||||
/* QA testing mode */
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
|
||||
qatest = 1;
|
||||
}
|
||||
|
||||
/* This will pick the best possible CUDA capable device */
|
||||
cudaDeviceProp deviceProp;
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
printf("GPU selected Device ID = %d \n", devID);
|
||||
|
||||
if (devID < 0) {
|
||||
printf("Invalid GPU device %d selected, exiting...\n", devID);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
/* Statistics about the GPU device */
|
||||
printf("> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\n\n",
|
||||
deviceProp.multiProcessorCount,
|
||||
deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
/* Generate a Laplace matrix in CSR (Compressed Sparse Row) format */
|
||||
M = N = 16384;
|
||||
nz = 5 * N - 4 * (int)sqrt((double)N);
|
||||
I = (int *)malloc(sizeof(int) * (N + 1)); // csr row pointers for matrix A
|
||||
J = (int *)malloc(sizeof(int) * nz); // csr column indices for matrix A
|
||||
val = (float *)malloc(sizeof(float) * nz); // csr values for matrix A
|
||||
x = (float *)malloc(sizeof(float) * N);
|
||||
rhs = (float *)malloc(sizeof(float) * N);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
rhs[i] = 0.0; // Initialize RHS
|
||||
x[i] = 0.0; // Initial solution approximation
|
||||
}
|
||||
|
||||
genLaplace(I, J, val, M, N, nz, rhs);
|
||||
|
||||
/* Create CUBLAS context */
|
||||
cublasHandle_t cublasHandle = NULL;
|
||||
checkCudaErrors(cublasCreate(&cublasHandle));
|
||||
|
||||
/* Create CUSPARSE context */
|
||||
cusparseHandle_t cusparseHandle = NULL;
|
||||
checkCudaErrors(cusparseCreate(&cusparseHandle));
|
||||
|
||||
/* Description of the A matrix */
|
||||
cusparseMatDescr_t descr = 0;
|
||||
checkCudaErrors(cusparseCreateMatDescr(&descr));
|
||||
checkCudaErrors(cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL));
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO));
|
||||
|
||||
/* Allocate required memory */
|
||||
checkCudaErrors(cudaMalloc((void **)&d_col, nz * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_row, (N + 1) * sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_val, nz * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_y, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_p, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_omega, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_valsILU0, nz * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_zm1, (N) * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_zm2, (N) * sizeof(float)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_rm2, (N) * sizeof(float)));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseDnVecDescr_t vecp = NULL, vecX = NULL, vecY = NULL, vecR = NULL, vecZM1 = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecp, N, d_p, CUDA_R_32F));
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecX, N, d_x, CUDA_R_32F));
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecY, N, d_y, CUDA_R_32F));
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecR, N, d_r, CUDA_R_32F));
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecZM1, N, d_zm1, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecomega = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecomega, N, d_omega, CUDA_R_32F));
|
||||
|
||||
/* Initialize problem data */
|
||||
checkCudaErrors(cudaMemcpy(d_col, J, nz * sizeof(int), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_row, I, (N + 1) * sizeof(int), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_val, val, nz * sizeof(float), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_val, val, nz * sizeof(float), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_r, rhs, N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
cusparseSpMatDescr_t matM_lower, matM_upper;
|
||||
cusparseFillMode_t fill_lower = CUSPARSE_FILL_MODE_LOWER;
|
||||
cusparseDiagType_t diag_unit = CUSPARSE_DIAG_TYPE_UNIT;
|
||||
cusparseFillMode_t fill_upper = CUSPARSE_FILL_MODE_UPPER;
|
||||
cusparseDiagType_t diag_non_unit = CUSPARSE_DIAG_TYPE_NON_UNIT;
|
||||
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
N,
|
||||
N,
|
||||
nz,
|
||||
d_row,
|
||||
d_col,
|
||||
d_val,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_32F));
|
||||
|
||||
/* Copy A data to ILU(0) vals as input*/
|
||||
checkCudaErrors(cudaMemcpy(d_valsILU0, d_val, nz * sizeof(float), cudaMemcpyDeviceToDevice));
|
||||
|
||||
// Lower Part
|
||||
checkCudaErrors(cusparseCreateCsr(&matM_lower,
|
||||
N,
|
||||
N,
|
||||
nz,
|
||||
d_row,
|
||||
d_col,
|
||||
d_valsILU0,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_32F));
|
||||
|
||||
checkCudaErrors(cusparseSpMatSetAttribute(matM_lower, CUSPARSE_SPMAT_FILL_MODE, &fill_lower, sizeof(fill_lower)));
|
||||
checkCudaErrors(cusparseSpMatSetAttribute(matM_lower, CUSPARSE_SPMAT_DIAG_TYPE, &diag_unit, sizeof(diag_unit)));
|
||||
// M_upper
|
||||
checkCudaErrors(cusparseCreateCsr(&matM_upper,
|
||||
N,
|
||||
N,
|
||||
nz,
|
||||
d_row,
|
||||
d_col,
|
||||
d_valsILU0,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_32F));
|
||||
checkCudaErrors(cusparseSpMatSetAttribute(matM_upper, CUSPARSE_SPMAT_FILL_MODE, &fill_upper, sizeof(fill_upper)));
|
||||
checkCudaErrors(
|
||||
cusparseSpMatSetAttribute(matM_upper, CUSPARSE_SPMAT_DIAG_TYPE, &diag_non_unit, sizeof(diag_non_unit)));
|
||||
|
||||
|
||||
/* Create ILU(0) info object */
|
||||
int bufferSizeLU = 0;
|
||||
size_t bufferSizeMV, bufferSizeL, bufferSizeU;
|
||||
void *d_bufferLU, *d_bufferMV, *d_bufferL, *d_bufferU;
|
||||
cusparseSpSVDescr_t spsvDescrL, spsvDescrU;
|
||||
cusparseMatDescr_t matLU;
|
||||
csrilu02Info_t infoILU = NULL;
|
||||
|
||||
checkCudaErrors(cusparseCreateCsrilu02Info(&infoILU));
|
||||
checkCudaErrors(cusparseCreateMatDescr(&matLU));
|
||||
checkCudaErrors(cusparseSetMatType(matLU, CUSPARSE_MATRIX_TYPE_GENERAL));
|
||||
checkCudaErrors(cusparseSetMatIndexBase(matLU, CUSPARSE_INDEX_BASE_ZERO));
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matA,
|
||||
vecp,
|
||||
&floatzero,
|
||||
vecomega,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSizeMV));
|
||||
checkCudaErrors(cudaMalloc(&d_bufferMV, bufferSizeMV));
|
||||
|
||||
checkCudaErrors(
|
||||
cusparseScsrilu02_bufferSize(cusparseHandle, N, nz, matLU, d_val, d_row, d_col, infoILU, &bufferSizeLU));
|
||||
checkCudaErrors(cudaMalloc(&d_bufferLU, bufferSizeLU));
|
||||
|
||||
checkCudaErrors(cusparseSpSV_createDescr(&spsvDescrL));
|
||||
checkCudaErrors(cusparseSpSV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matM_lower,
|
||||
vecR,
|
||||
vecX,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPSV_ALG_DEFAULT,
|
||||
spsvDescrL,
|
||||
&bufferSizeL));
|
||||
checkCudaErrors(cudaMalloc(&d_bufferL, bufferSizeL));
|
||||
|
||||
checkCudaErrors(cusparseSpSV_createDescr(&spsvDescrU));
|
||||
checkCudaErrors(cusparseSpSV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matM_upper,
|
||||
vecR,
|
||||
vecX,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPSV_ALG_DEFAULT,
|
||||
spsvDescrU,
|
||||
&bufferSizeU));
|
||||
checkCudaErrors(cudaMalloc(&d_bufferU, bufferSizeU));
|
||||
|
||||
/* Conjugate gradient without preconditioning.
|
||||
------------------------------------------
|
||||
|
||||
Follows the description by Golub & Van Loan,
|
||||
"Matrix Computations 3rd ed.", Section 10.2.6 */
|
||||
|
||||
printf("Convergence of CG without preconditioning: \n");
|
||||
k = 0;
|
||||
r0 = 0;
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, &r1));
|
||||
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
k++;
|
||||
|
||||
if (k == 1) {
|
||||
checkCudaErrors(cublasScopy(cublasHandle, N, d_r, 1, d_p, 1));
|
||||
}
|
||||
else {
|
||||
beta = r1 / r0;
|
||||
checkCudaErrors(cublasSscal(cublasHandle, N, &beta, d_p, 1));
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &floatone, d_r, 1, d_p, 1));
|
||||
}
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matA,
|
||||
vecp,
|
||||
&floatzero,
|
||||
vecomega,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
d_bufferMV));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_omega, 1, &dot));
|
||||
alpha = r1 / dot;
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpha, d_p, 1, d_x, 1));
|
||||
nalpha = -alpha;
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &nalpha, d_omega, 1, d_r, 1));
|
||||
r0 = r1;
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, &r1));
|
||||
}
|
||||
|
||||
printf(" iteration = %3d, residual = %e \n", k, sqrt(r1));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost));
|
||||
|
||||
/* check result */
|
||||
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[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs[i]);
|
||||
|
||||
if (diff > err) {
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
printf(" Convergence Test: %s \n", (k <= max_iter) ? "OK" : "FAIL");
|
||||
nErrors += (k > max_iter) ? 1 : 0;
|
||||
qaerr1 = err;
|
||||
|
||||
if (0) {
|
||||
// output result in matlab-style array
|
||||
int n = (int)sqrt((double)N);
|
||||
printf("a = [ ");
|
||||
|
||||
for (int iy = 0; iy < n; iy++) {
|
||||
for (int ix = 0; ix < n; ix++) {
|
||||
printf(" %f ", x[iy * n + ix]);
|
||||
}
|
||||
|
||||
if (iy == n - 1) {
|
||||
printf(" ]");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Preconditioned Conjugate Gradient using ILU.
|
||||
--------------------------------------------
|
||||
Follows the description by Golub & Van Loan,
|
||||
"Matrix Computations 3rd ed.", Algorithm 10.3.1 */
|
||||
|
||||
printf("\nConvergence of CG using ILU(0) preconditioning: \n");
|
||||
|
||||
/* Perform analysis for ILU(0) */
|
||||
checkCudaErrors(cusparseScsrilu02_analysis(
|
||||
cusparseHandle, N, nz, descr, d_valsILU0, d_row, d_col, infoILU, CUSPARSE_SOLVE_POLICY_USE_LEVEL, d_bufferLU));
|
||||
|
||||
/* generate the ILU(0) factors */
|
||||
checkCudaErrors(cusparseScsrilu02(
|
||||
cusparseHandle, N, nz, matLU, d_valsILU0, d_row, d_col, infoILU, CUSPARSE_SOLVE_POLICY_USE_LEVEL, d_bufferLU));
|
||||
|
||||
/* perform triangular solve analysis */
|
||||
checkCudaErrors(cusparseSpSV_analysis(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matM_lower,
|
||||
vecR,
|
||||
vecX,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPSV_ALG_DEFAULT,
|
||||
spsvDescrL,
|
||||
d_bufferL));
|
||||
|
||||
checkCudaErrors(cusparseSpSV_analysis(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matM_upper,
|
||||
vecR,
|
||||
vecX,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPSV_ALG_DEFAULT,
|
||||
spsvDescrU,
|
||||
d_bufferU));
|
||||
|
||||
/* reset the initial guess of the solution to zero */
|
||||
for (int i = 0; i < N; i++) {
|
||||
x[i] = 0.0;
|
||||
}
|
||||
checkCudaErrors(cudaMemcpy(d_r, rhs, N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
|
||||
k = 0;
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, &r1));
|
||||
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
// preconditioner application: d_zm1 = U^-1 L^-1 d_r
|
||||
checkCudaErrors(cusparseSpSV_solve(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matM_lower,
|
||||
vecR,
|
||||
vecY,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPSV_ALG_DEFAULT,
|
||||
spsvDescrL));
|
||||
|
||||
checkCudaErrors(cusparseSpSV_solve(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matM_upper,
|
||||
vecY,
|
||||
vecZM1,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPSV_ALG_DEFAULT,
|
||||
spsvDescrU));
|
||||
k++;
|
||||
|
||||
if (k == 1) {
|
||||
checkCudaErrors(cublasScopy(cublasHandle, N, d_zm1, 1, d_p, 1));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_zm1, 1, &numerator));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_rm2, 1, d_zm2, 1, &denominator));
|
||||
beta = numerator / denominator;
|
||||
checkCudaErrors(cublasSscal(cublasHandle, N, &beta, d_p, 1));
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &floatone, d_zm1, 1, d_p, 1));
|
||||
}
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&floatone,
|
||||
matA,
|
||||
vecp,
|
||||
&floatzero,
|
||||
vecomega,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
d_bufferMV));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_zm1, 1, &numerator));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_omega, 1, &denominator));
|
||||
alpha = numerator / denominator;
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpha, d_p, 1, d_x, 1));
|
||||
checkCudaErrors(cublasScopy(cublasHandle, N, d_r, 1, d_rm2, 1));
|
||||
checkCudaErrors(cublasScopy(cublasHandle, N, d_zm1, 1, d_zm2, 1));
|
||||
nalpha = -alpha;
|
||||
checkCudaErrors(cublasSaxpy(cublasHandle, N, &nalpha, d_omega, 1, d_r, 1));
|
||||
checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, &r1));
|
||||
}
|
||||
|
||||
printf(" iteration = %3d, residual = %e \n", k, sqrt(r1));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost));
|
||||
|
||||
/* check result */
|
||||
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[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs[i]);
|
||||
|
||||
if (diff > err) {
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
printf(" Convergence Test: %s \n", (k <= max_iter) ? "OK" : "FAIL");
|
||||
nErrors += (k > max_iter) ? 1 : 0;
|
||||
qaerr2 = err;
|
||||
|
||||
/* Destroy descriptors */
|
||||
checkCudaErrors(cusparseDestroyCsrilu02Info(infoILU));
|
||||
checkCudaErrors(cusparseDestroyMatDescr(matLU));
|
||||
checkCudaErrors(cusparseSpSV_destroyDescr(spsvDescrL));
|
||||
checkCudaErrors(cusparseSpSV_destroyDescr(spsvDescrU));
|
||||
checkCudaErrors(cusparseDestroySpMat(matM_lower));
|
||||
checkCudaErrors(cusparseDestroySpMat(matM_upper));
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecp));
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecomega));
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecR));
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecX));
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecY));
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecZM1));
|
||||
|
||||
/* Destroy contexts */
|
||||
checkCudaErrors(cusparseDestroy(cusparseHandle));
|
||||
checkCudaErrors(cublasDestroy(cublasHandle));
|
||||
|
||||
/* Free device memory */
|
||||
free(I);
|
||||
free(J);
|
||||
free(val);
|
||||
free(x);
|
||||
free(rhs);
|
||||
checkCudaErrors(cudaFree(d_bufferMV));
|
||||
checkCudaErrors(cudaFree(d_bufferLU));
|
||||
checkCudaErrors(cudaFree(d_bufferL));
|
||||
checkCudaErrors(cudaFree(d_bufferU));
|
||||
checkCudaErrors(cudaFree(d_col));
|
||||
checkCudaErrors(cudaFree(d_row));
|
||||
checkCudaErrors(cudaFree(d_val));
|
||||
checkCudaErrors(cudaFree(d_x));
|
||||
checkCudaErrors(cudaFree(d_y));
|
||||
checkCudaErrors(cudaFree(d_r));
|
||||
checkCudaErrors(cudaFree(d_p));
|
||||
checkCudaErrors(cudaFree(d_omega));
|
||||
checkCudaErrors(cudaFree(d_valsILU0));
|
||||
checkCudaErrors(cudaFree(d_zm1));
|
||||
checkCudaErrors(cudaFree(d_zm2));
|
||||
checkCudaErrors(cudaFree(d_rm2));
|
||||
|
||||
// cudaDeviceReset causes the driver to clean up all state. While
|
||||
// not mandatory in normal operation, it is good practice. It is also
|
||||
// needed to ensure correct operation when the application is being
|
||||
// profiled. Calling cudaDeviceReset causes all profile data to be
|
||||
// flushed before the application exits
|
||||
cudaDeviceReset();
|
||||
|
||||
printf("\n");
|
||||
printf("Test Summary:\n");
|
||||
printf(" Counted total of %d errors\n", nErrors);
|
||||
printf(" qaerr1 = %f qaerr2 = %f\n\n", fabs(qaerr1), fabs(qaerr2));
|
||||
exit((nErrors == 0 && fabs(qaerr1) < 1e-5 && fabs(qaerr2) < 1e-5 ? EXIT_SUCCESS : EXIT_FAILURE));
|
||||
}
|
||||
18
cpp/4_CUDA_Libraries/conjugateGradientUM/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/conjugateGradientUM/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/conjugateGradientUM/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/conjugateGradientUM/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
44
cpp/4_CUDA_Libraries/conjugateGradientUM/CMakeLists.txt
Normal file
44
cpp/4_CUDA_Libraries/conjugateGradientUM/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(conjugateGradientUM LANGUAGES CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for conjugateGradientUM
|
||||
add_executable(conjugateGradientUM main.cpp)
|
||||
|
||||
target_compile_options(conjugateGradientUM PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(conjugateGradientUM PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(conjugateGradientUM PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(conjugateGradientUM PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(conjugateGradientUM PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusparse
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
34
cpp/4_CUDA_Libraries/conjugateGradientUM/README.md
Normal file
34
cpp/4_CUDA_Libraries/conjugateGradientUM/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# conjugateGradientUM - ConjugateGradientUM
|
||||
|
||||
## Description
|
||||
|
||||
This sample implements a conjugate gradient solver on GPU using CUBLAS and CUSPARSE library, using Unified Memory
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Unified Memory, Linear Algebra, CUBLAS Library, CUSPARSE Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaFree, cudaMallocManaged, cudaDeviceSynchronize, cudaMalloc, cudaGetDeviceProperties
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[UVM](../../../README.md#unified-virtual-memory), [CUBLAS](../../../README.md#cublas), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
296
cpp/4_CUDA_Libraries/conjugateGradientUM/main.cpp
Normal file
296
cpp/4_CUDA_Libraries/conjugateGradientUM/main.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
/* 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 GPU
|
||||
* using CUBLAS and CUSPARSE
|
||||
*
|
||||
*/
|
||||
|
||||
// includes, system
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Using updated (v2) interfaces to cublas and cusparse */
|
||||
#include <cublas_v2.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <cusparse.h>
|
||||
|
||||
// Utilities and system includes
|
||||
#include <helper_cuda.h> // helper function CUDA error checking and initialization
|
||||
#include <helper_functions.h> // helper for shared functions common to CUDA Samples
|
||||
|
||||
const char *sSDKname = "conjugateGradientUM";
|
||||
|
||||
/* genTridiag: generate a random tridiagonal symmetric matrix */
|
||||
void genTridiag(int *I, int *J, 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;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int N = 0, nz = 0, *I = NULL, *J = NULL;
|
||||
float *val = NULL;
|
||||
const float tol = 1e-5f;
|
||||
const int max_iter = 10000;
|
||||
float *x;
|
||||
float *rhs;
|
||||
float a, b, na, r0, r1;
|
||||
float dot;
|
||||
float *r, *p, *Ax;
|
||||
int k;
|
||||
float alpha, beta, alpham1;
|
||||
|
||||
printf("Starting [%s]...\n", sSDKname);
|
||||
|
||||
// This will pick the best possible CUDA capable device
|
||||
cudaDeviceProp deviceProp;
|
||||
int devID = findCudaDevice(argc, (const char **)argv);
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
|
||||
|
||||
if (!deviceProp.managedMemory) {
|
||||
// This samples requires being run on a device that supports Unified Memory
|
||||
fprintf(stderr, "Unified Memory not supported on this device\n");
|
||||
exit(EXIT_WAIVED);
|
||||
}
|
||||
|
||||
// Statistics about the GPU device
|
||||
printf("> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\n\n",
|
||||
deviceProp.multiProcessorCount,
|
||||
deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
/* Generate a random tridiagonal symmetric matrix in CSR format */
|
||||
N = 1048576;
|
||||
nz = (N - 2) * 3 + 4;
|
||||
|
||||
cudaMallocManaged((void **)&I, sizeof(int) * (N + 1));
|
||||
cudaMallocManaged((void **)&J, sizeof(int) * nz);
|
||||
cudaMallocManaged((void **)&val, sizeof(float) * nz);
|
||||
|
||||
genTridiag(I, J, val, N, nz);
|
||||
|
||||
cudaMallocManaged((void **)&x, sizeof(float) * N);
|
||||
cudaMallocManaged((void **)&rhs, sizeof(float) * N);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
rhs[i] = 1.0;
|
||||
x[i] = 0.0;
|
||||
}
|
||||
|
||||
/* Get handle to the CUBLAS context */
|
||||
cublasHandle_t cublasHandle = 0;
|
||||
cublasStatus_t cublasStatus;
|
||||
cublasStatus = cublasCreate(&cublasHandle);
|
||||
|
||||
checkCudaErrors(cublasStatus);
|
||||
|
||||
/* Get handle to the CUSPARSE context */
|
||||
cusparseHandle_t cusparseHandle = 0;
|
||||
cusparseStatus_t cusparseStatus;
|
||||
cusparseStatus = cusparseCreate(&cusparseHandle);
|
||||
|
||||
checkCudaErrors(cusparseStatus);
|
||||
|
||||
cusparseMatDescr_t descr = 0;
|
||||
cusparseStatus = cusparseCreateMatDescr(&descr);
|
||||
|
||||
checkCudaErrors(cusparseStatus);
|
||||
|
||||
cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL);
|
||||
cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO);
|
||||
|
||||
// temp memory for CG
|
||||
checkCudaErrors(cudaMallocManaged((void **)&r, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMallocManaged((void **)&p, N * sizeof(float)));
|
||||
checkCudaErrors(cudaMallocManaged((void **)&Ax, N * sizeof(float)));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
checkCudaErrors(cusparseCreateCsr(
|
||||
&matA, N, N, nz, I, J, val, CUSPARSE_INDEX_32I, CUSPARSE_INDEX_32I, CUSPARSE_INDEX_BASE_ZERO, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecx, N, x, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecp = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecp, N, p, CUDA_R_32F));
|
||||
cusparseDnVecDescr_t vecAx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecAx, N, Ax, CUDA_R_32F));
|
||||
|
||||
cudaDeviceSynchronize();
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
r[i] = rhs[i];
|
||||
}
|
||||
|
||||
alpha = 1.0;
|
||||
alpham1 = -1.0;
|
||||
beta = 0.0;
|
||||
r0 = 0.;
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
size_t bufferSize = 0;
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecx,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSize));
|
||||
void *buffer = NULL;
|
||||
checkCudaErrors(cudaMalloc(&buffer, bufferSize));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecx,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
cublasSaxpy(cublasHandle, N, &alpham1, Ax, 1, r, 1);
|
||||
cublasStatus = cublasSdot(cublasHandle, N, r, 1, r, 1, &r1);
|
||||
|
||||
k = 1;
|
||||
|
||||
while (r1 > tol * tol && k <= max_iter) {
|
||||
if (k > 1) {
|
||||
b = r1 / r0;
|
||||
cublasStatus = cublasSscal(cublasHandle, N, &b, p, 1);
|
||||
cublasStatus = cublasSaxpy(cublasHandle, N, &alpha, r, 1, p, 1);
|
||||
}
|
||||
else {
|
||||
cublasStatus = cublasScopy(cublasHandle, N, r, 1, p, 1);
|
||||
}
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&alpha,
|
||||
matA,
|
||||
vecp,
|
||||
&beta,
|
||||
vecAx,
|
||||
CUDA_R_32F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
cublasStatus = cublasSdot(cublasHandle, N, p, 1, Ax, 1, &dot);
|
||||
a = r1 / dot;
|
||||
|
||||
cublasStatus = cublasSaxpy(cublasHandle, N, &a, p, 1, x, 1);
|
||||
na = -a;
|
||||
cublasStatus = cublasSaxpy(cublasHandle, N, &na, Ax, 1, r, 1);
|
||||
|
||||
r0 = r1;
|
||||
cublasStatus = cublasSdot(cublasHandle, N, r, 1, r, 1, &r1);
|
||||
cudaDeviceSynchronize();
|
||||
printf("iteration = %3d, residual = %e\n", k, sqrt(r1));
|
||||
k++;
|
||||
}
|
||||
|
||||
printf("Final residual: %e\n", sqrt(r1));
|
||||
|
||||
fprintf(stdout, "&&&& conjugateGradientUM %s\n", (sqrt(r1) < tol) ? "PASSED" : "FAILED");
|
||||
|
||||
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[j] * x[J[j]];
|
||||
}
|
||||
|
||||
diff = fabs(rsum - rhs[i]);
|
||||
|
||||
if (diff > err) {
|
||||
err = diff;
|
||||
}
|
||||
}
|
||||
|
||||
cusparseDestroy(cusparseHandle);
|
||||
cublasDestroy(cublasHandle);
|
||||
if (matA) {
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
}
|
||||
if (vecx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecx));
|
||||
}
|
||||
if (vecAx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecAx));
|
||||
}
|
||||
if (vecp) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecp));
|
||||
}
|
||||
|
||||
cudaFree(I);
|
||||
cudaFree(J);
|
||||
cudaFree(val);
|
||||
cudaFree(x);
|
||||
cudaFree(rhs);
|
||||
cudaFree(r);
|
||||
cudaFree(p);
|
||||
cudaFree(Ax);
|
||||
|
||||
printf("Test Summary: Error amount = %f, result = %s\n", err, (k <= max_iter) ? "SUCCESS" : "FAILURE");
|
||||
exit((k <= max_iter) ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
18
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
56
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/CMakeLists.txt
Normal file
56
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cuSolverDn_LinearSolver LANGUAGES C CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cuSolverDn_LinearSolver
|
||||
add_executable(cuSolverDn_LinearSolver cuSolverDn_LinearSolver.cpp mmio.c mmio_wrapper.cpp)
|
||||
|
||||
target_compile_options(cuSolverDn_LinearSolver PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cuSolverDn_LinearSolver PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cuSolverDn_LinearSolver PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(cuSolverDn_LinearSolver PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(cuSolverDn_LinearSolver PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusolver
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverDn_LinearSolver POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gr_900_900_crg.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
add_custom_command(TARGET cuSolverDn_LinearSolver POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap3D_7pt_n20.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
39
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/README.md
Normal file
39
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# cuSolverDn_LinearSolver - cuSolverDn Linear Solver
|
||||
|
||||
## Description
|
||||
|
||||
A CUDA Sample that demonstrates cuSolverDN's LU, QR and Cholesky factorization.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUSOLVER Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, aarch64
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuDoubleComplex, cuComplex
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaStreamDestroy, cudaFree, cudaDeviceSynchronize, cudaMemset, cudaMalloc, cudaStreamCreate
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUSOLVER](../../../README.md#cusolver), [CUBLAS](../../../README.md#cublas), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,587 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Test three linear solvers, including Cholesky, LU and QR.
|
||||
* The user has to prepare a sparse matrix of "matrix market format" (with
|
||||
* extension .mtx). For example, the user can download matrices in Florida
|
||||
* Sparse Matrix Collection.
|
||||
* (http://www.cise.ufl.edu/research/sparse/matrices/)
|
||||
*
|
||||
* The user needs to choose a solver by switch -R<solver> and
|
||||
* to provide the path of the matrix by switch -F<file>, then
|
||||
* the program solves
|
||||
* A*x = b where b = ones(m,1)
|
||||
* and reports relative error
|
||||
* |b-A*x|/(|A|*|x|)
|
||||
*
|
||||
* The elapsed time is also reported so the user can compare efficiency of
|
||||
* different solvers.
|
||||
*
|
||||
* How to use
|
||||
* ./cuSolverDn_LinearSolver // Default: cholesky
|
||||
* ./cuSolverDn_LinearSolver -R=chol -filefile> // cholesky factorization
|
||||
* ./cuSolverDn_LinearSolver -R=lu -file<file> // LU with partial
|
||||
* pivoting
|
||||
* ./cuSolverDn_LinearSolver -R=qr -file<file> // QR factorization
|
||||
*
|
||||
* Remark: the absolute error on solution x is meaningless without knowing
|
||||
* condition number of A. The relative error on residual should be close to
|
||||
* machine zero, i.e. 1.e-15.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cublas_v2.h"
|
||||
#include "cusolverDn.h"
|
||||
#include "helper_cuda.h"
|
||||
#include "helper_cusolver.h"
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
void UsageDN(void)
|
||||
{
|
||||
printf("<options>\n");
|
||||
printf("-h : display this help\n");
|
||||
printf("-R=<name> : choose a linear solver\n");
|
||||
printf(" chol (cholesky factorization), this is default\n");
|
||||
printf(" qr (QR factorization)\n");
|
||||
printf(" lu (LU factorization)\n");
|
||||
printf("-lda=<int> : leading dimension of A , m by default\n");
|
||||
printf("-file=<filename>: filename containing a matrix in MM format\n");
|
||||
printf("-device=<device_id> : <device_id> if want to run on specific GPU\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* solve A*x = b by Cholesky factorization
|
||||
*
|
||||
*/
|
||||
int linearSolverCHOL(cusolverDnHandle_t handle, int n, const double *Acopy, int lda, const double *b, double *x)
|
||||
{
|
||||
int bufferSize = 0;
|
||||
int *info = NULL;
|
||||
double *buffer = NULL;
|
||||
double *A = NULL;
|
||||
int h_info = 0;
|
||||
double start, stop;
|
||||
double time_solve;
|
||||
cublasFillMode_t uplo = CUBLAS_FILL_MODE_LOWER;
|
||||
|
||||
checkCudaErrors(cusolverDnDpotrf_bufferSize(handle, uplo, n, (double *)Acopy, lda, &bufferSize));
|
||||
|
||||
checkCudaErrors(cudaMalloc(&info, sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc(&buffer, sizeof(double) * bufferSize));
|
||||
checkCudaErrors(cudaMalloc(&A, sizeof(double) * lda * n));
|
||||
|
||||
// prepare a copy of A because potrf will overwrite A with L
|
||||
checkCudaErrors(cudaMemcpy(A, Acopy, sizeof(double) * lda * n, cudaMemcpyDeviceToDevice));
|
||||
checkCudaErrors(cudaMemset(info, 0, sizeof(int)));
|
||||
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverDnDpotrf(handle, uplo, n, A, lda, buffer, bufferSize, info));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(&h_info, info, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
|
||||
if (0 != h_info) {
|
||||
fprintf(stderr, "Error: Cholesky factorization failed\n");
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaMemcpy(x, b, sizeof(double) * n, cudaMemcpyDeviceToDevice));
|
||||
|
||||
checkCudaErrors(cusolverDnDpotrs(handle, uplo, n, 1, A, lda, x, n, info));
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
|
||||
time_solve = stop - start;
|
||||
fprintf(stdout, "timing: cholesky = %10.6f sec\n", time_solve);
|
||||
|
||||
if (info) {
|
||||
checkCudaErrors(cudaFree(info));
|
||||
}
|
||||
if (buffer) {
|
||||
checkCudaErrors(cudaFree(buffer));
|
||||
}
|
||||
if (A) {
|
||||
checkCudaErrors(cudaFree(A));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* solve A*x = b by LU with partial pivoting
|
||||
*
|
||||
*/
|
||||
int linearSolverLU(cusolverDnHandle_t handle, int n, const double *Acopy, int lda, const double *b, double *x)
|
||||
{
|
||||
int bufferSize = 0;
|
||||
int *info = NULL;
|
||||
double *buffer = NULL;
|
||||
double *A = NULL;
|
||||
int *ipiv = NULL; // pivoting sequence
|
||||
int h_info = 0;
|
||||
double start, stop;
|
||||
double time_solve;
|
||||
|
||||
checkCudaErrors(cusolverDnDgetrf_bufferSize(handle, n, n, (double *)Acopy, lda, &bufferSize));
|
||||
|
||||
checkCudaErrors(cudaMalloc(&info, sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc(&buffer, sizeof(double) * bufferSize));
|
||||
checkCudaErrors(cudaMalloc(&A, sizeof(double) * lda * n));
|
||||
checkCudaErrors(cudaMalloc(&ipiv, sizeof(int) * n));
|
||||
|
||||
// prepare a copy of A because getrf will overwrite A with L
|
||||
checkCudaErrors(cudaMemcpy(A, Acopy, sizeof(double) * lda * n, cudaMemcpyDeviceToDevice));
|
||||
checkCudaErrors(cudaMemset(info, 0, sizeof(int)));
|
||||
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverDnDgetrf(handle, n, n, A, lda, buffer, ipiv, info));
|
||||
checkCudaErrors(cudaMemcpy(&h_info, info, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
|
||||
if (0 != h_info) {
|
||||
fprintf(stderr, "Error: LU factorization failed\n");
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaMemcpy(x, b, sizeof(double) * n, cudaMemcpyDeviceToDevice));
|
||||
checkCudaErrors(cusolverDnDgetrs(handle, CUBLAS_OP_N, n, 1, A, lda, ipiv, x, n, info));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
|
||||
time_solve = stop - start;
|
||||
fprintf(stdout, "timing: LU = %10.6f sec\n", time_solve);
|
||||
|
||||
if (info) {
|
||||
checkCudaErrors(cudaFree(info));
|
||||
}
|
||||
if (buffer) {
|
||||
checkCudaErrors(cudaFree(buffer));
|
||||
}
|
||||
if (A) {
|
||||
checkCudaErrors(cudaFree(A));
|
||||
}
|
||||
if (ipiv) {
|
||||
checkCudaErrors(cudaFree(ipiv));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* solve A*x = b by QR
|
||||
*
|
||||
*/
|
||||
int linearSolverQR(cusolverDnHandle_t handle, int n, const double *Acopy, int lda, const double *b, double *x)
|
||||
{
|
||||
cublasHandle_t cublasHandle = NULL; // used in residual evaluation
|
||||
int bufferSize = 0;
|
||||
int bufferSize_geqrf = 0;
|
||||
int bufferSize_ormqr = 0;
|
||||
int *info = NULL;
|
||||
double *buffer = NULL;
|
||||
double *A = NULL;
|
||||
double *tau = NULL;
|
||||
int h_info = 0;
|
||||
double start, stop;
|
||||
double time_solve;
|
||||
const double one = 1.0;
|
||||
|
||||
checkCudaErrors(cublasCreate(&cublasHandle));
|
||||
|
||||
checkCudaErrors(cusolverDnDgeqrf_bufferSize(handle, n, n, (double *)Acopy, lda, &bufferSize_geqrf));
|
||||
checkCudaErrors(cusolverDnDormqr_bufferSize(
|
||||
handle, CUBLAS_SIDE_LEFT, CUBLAS_OP_T, n, 1, n, A, lda, NULL, x, n, &bufferSize_ormqr));
|
||||
|
||||
printf("buffer_geqrf = %d, buffer_ormqr = %d \n", bufferSize_geqrf, bufferSize_ormqr);
|
||||
|
||||
bufferSize = (bufferSize_geqrf > bufferSize_ormqr) ? bufferSize_geqrf : bufferSize_ormqr;
|
||||
|
||||
checkCudaErrors(cudaMalloc(&info, sizeof(int)));
|
||||
checkCudaErrors(cudaMalloc(&buffer, sizeof(double) * bufferSize));
|
||||
checkCudaErrors(cudaMalloc(&A, sizeof(double) * lda * n));
|
||||
checkCudaErrors(cudaMalloc((void **)&tau, sizeof(double) * n));
|
||||
|
||||
// prepare a copy of A because getrf will overwrite A with L
|
||||
checkCudaErrors(cudaMemcpy(A, Acopy, sizeof(double) * lda * n, cudaMemcpyDeviceToDevice));
|
||||
|
||||
checkCudaErrors(cudaMemset(info, 0, sizeof(int)));
|
||||
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
// compute QR factorization
|
||||
checkCudaErrors(cusolverDnDgeqrf(handle, n, n, A, lda, tau, buffer, bufferSize, info));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(&h_info, info, sizeof(int), cudaMemcpyDeviceToHost));
|
||||
|
||||
if (0 != h_info) {
|
||||
fprintf(stderr, "Error: QR factorization failed\n");
|
||||
}
|
||||
|
||||
checkCudaErrors(cudaMemcpy(x, b, sizeof(double) * n, cudaMemcpyDeviceToDevice));
|
||||
|
||||
// compute Q^T*b
|
||||
checkCudaErrors(
|
||||
cusolverDnDormqr(handle, CUBLAS_SIDE_LEFT, CUBLAS_OP_T, n, 1, n, A, lda, tau, x, n, buffer, bufferSize, info));
|
||||
|
||||
// x = R \ Q^T*b
|
||||
checkCudaErrors(cublasDtrsm(cublasHandle,
|
||||
CUBLAS_SIDE_LEFT,
|
||||
CUBLAS_FILL_MODE_UPPER,
|
||||
CUBLAS_OP_N,
|
||||
CUBLAS_DIAG_NON_UNIT,
|
||||
n,
|
||||
1,
|
||||
&one,
|
||||
A,
|
||||
lda,
|
||||
x,
|
||||
n));
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
|
||||
time_solve = stop - start;
|
||||
fprintf(stdout, "timing: QR = %10.6f sec\n", time_solve);
|
||||
|
||||
if (cublasHandle) {
|
||||
checkCudaErrors(cublasDestroy(cublasHandle));
|
||||
}
|
||||
if (info) {
|
||||
checkCudaErrors(cudaFree(info));
|
||||
}
|
||||
if (buffer) {
|
||||
checkCudaErrors(cudaFree(buffer));
|
||||
}
|
||||
if (A) {
|
||||
checkCudaErrors(cudaFree(A));
|
||||
}
|
||||
if (tau) {
|
||||
checkCudaErrors(cudaFree(tau));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parseCommandLineArguments(int argc, char *argv[], struct testOpts &opts)
|
||||
{
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "-h")) {
|
||||
UsageDN();
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "R")) {
|
||||
char *solverType = NULL;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "R", &solverType);
|
||||
|
||||
if (solverType) {
|
||||
if ((STRCASECMP(solverType, "chol") != 0) && (STRCASECMP(solverType, "lu") != 0)
|
||||
&& (STRCASECMP(solverType, "qr") != 0)) {
|
||||
printf("\nIncorrect argument passed to -R option\n");
|
||||
UsageDN();
|
||||
}
|
||||
else {
|
||||
opts.testFunc = solverType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
|
||||
char *fileName = 0;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "file", &fileName);
|
||||
|
||||
if (fileName) {
|
||||
opts.sparse_mat_filename = fileName;
|
||||
}
|
||||
else {
|
||||
printf("\nIncorrect filename passed to -file \n ");
|
||||
UsageDN();
|
||||
}
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "lda")) {
|
||||
opts.lda = getCmdLineArgumentInt(argc, (const char **)argv, "lda");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct testOpts opts;
|
||||
cusolverDnHandle_t handle = NULL;
|
||||
cublasHandle_t cublasHandle = NULL; // used in residual evaluation
|
||||
cudaStream_t stream = NULL;
|
||||
|
||||
int rowsA = 0; // number of rows of A
|
||||
int colsA = 0; // number of columns of A
|
||||
int nnzA = 0; // number of nonzeros of A
|
||||
int baseA = 0; // base index in CSR format
|
||||
int lda = 0; // leading dimension in dense matrix
|
||||
|
||||
// CSR(A) from I/O
|
||||
int *h_csrRowPtrA = NULL;
|
||||
int *h_csrColIndA = NULL;
|
||||
double *h_csrValA = NULL;
|
||||
|
||||
double *h_A = NULL; // dense matrix from CSR(A)
|
||||
double *h_x = NULL; // a copy of d_x
|
||||
double *h_b = NULL; // b = ones(m,1)
|
||||
double *h_r = NULL; // r = b - A*x, a copy of d_r
|
||||
|
||||
double *d_A = NULL; // a copy of h_A
|
||||
double *d_x = NULL; // x = A \ b
|
||||
double *d_b = NULL; // a copy of h_b
|
||||
double *d_r = NULL; // r = b - A*x
|
||||
|
||||
// the constants are used in residual evaluation, r = b - A*x
|
||||
const double minus_one = -1.0;
|
||||
const double one = 1.0;
|
||||
|
||||
double x_inf = 0.0;
|
||||
double r_inf = 0.0;
|
||||
double A_inf = 0.0;
|
||||
int errors = 0;
|
||||
|
||||
parseCommandLineArguments(argc, argv, opts);
|
||||
|
||||
if (NULL == opts.testFunc) {
|
||||
opts.testFunc = "chol"; // By default running Cholesky as NO solver
|
||||
// selected with -R option.
|
||||
}
|
||||
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
printf("step 1: read matrix market format\n");
|
||||
|
||||
if (opts.sparse_mat_filename == NULL) {
|
||||
opts.sparse_mat_filename = sdkFindFilePath("gr_900_900_crg.mtx", argv[0]);
|
||||
if (opts.sparse_mat_filename != NULL)
|
||||
printf("Using default input file [%s]\n", opts.sparse_mat_filename);
|
||||
else
|
||||
printf("Could not find gr_900_900_crg.mtx\n");
|
||||
}
|
||||
else {
|
||||
printf("Using input file [%s]\n", opts.sparse_mat_filename);
|
||||
}
|
||||
|
||||
if (opts.sparse_mat_filename == NULL) {
|
||||
fprintf(stderr, "Error: input matrix is not provided\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (loadMMSparseMatrix<double>(opts.sparse_mat_filename,
|
||||
'd',
|
||||
true,
|
||||
&rowsA,
|
||||
&colsA,
|
||||
&nnzA,
|
||||
&h_csrValA,
|
||||
&h_csrRowPtrA,
|
||||
&h_csrColIndA,
|
||||
true)) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
baseA = h_csrRowPtrA[0]; // baseA = {0,1}
|
||||
|
||||
printf("sparse matrix A is %d x %d with %d nonzeros, base=%d\n", rowsA, colsA, nnzA, baseA);
|
||||
|
||||
if (rowsA != colsA) {
|
||||
fprintf(stderr, "Error: only support square matrix\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("step 2: convert CSR(A) to dense matrix\n");
|
||||
|
||||
lda = opts.lda ? opts.lda : rowsA;
|
||||
if (lda < rowsA) {
|
||||
fprintf(stderr, "Error: lda must be greater or equal to dimension of A\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
h_A = (double *)malloc(sizeof(double) * lda * colsA);
|
||||
h_x = (double *)malloc(sizeof(double) * colsA);
|
||||
h_b = (double *)malloc(sizeof(double) * rowsA);
|
||||
h_r = (double *)malloc(sizeof(double) * rowsA);
|
||||
assert(NULL != h_A);
|
||||
assert(NULL != h_x);
|
||||
assert(NULL != h_b);
|
||||
assert(NULL != h_r);
|
||||
|
||||
memset(h_A, 0, sizeof(double) * lda * colsA);
|
||||
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
const int start = h_csrRowPtrA[row] - baseA;
|
||||
const int end = h_csrRowPtrA[row + 1] - baseA;
|
||||
for (int colidx = start; colidx < end; colidx++) {
|
||||
const int col = h_csrColIndA[colidx] - baseA;
|
||||
const double Areg = h_csrValA[colidx];
|
||||
h_A[row + col * lda] = Areg;
|
||||
}
|
||||
}
|
||||
|
||||
printf("step 3: set right hand side vector (b) to 1\n");
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
h_b[row] = 1.0;
|
||||
}
|
||||
|
||||
// verify if A is symmetric or not.
|
||||
if (0 == strcmp(opts.testFunc, "chol")) {
|
||||
int issym = 1;
|
||||
for (int j = 0; j < colsA; j++) {
|
||||
for (int i = j; i < rowsA; i++) {
|
||||
double Aij = h_A[i + j * lda];
|
||||
double Aji = h_A[j + i * lda];
|
||||
if (Aij != Aji) {
|
||||
issym = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!issym) {
|
||||
printf("Error: A has no symmetric pattern, please use LU or QR \n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
checkCudaErrors(cusolverDnCreate(&handle));
|
||||
checkCudaErrors(cublasCreate(&cublasHandle));
|
||||
checkCudaErrors(cudaStreamCreate(&stream));
|
||||
|
||||
checkCudaErrors(cusolverDnSetStream(handle, stream));
|
||||
checkCudaErrors(cublasSetStream(cublasHandle, stream));
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&d_A, sizeof(double) * lda * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, sizeof(double) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_b, sizeof(double) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, sizeof(double) * rowsA));
|
||||
|
||||
printf("step 4: prepare data on device\n");
|
||||
checkCudaErrors(cudaMemcpy(d_A, h_A, sizeof(double) * lda * colsA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_b, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
|
||||
printf("step 5: solve A*x = b \n");
|
||||
// d_A and d_b are read-only
|
||||
if (0 == strcmp(opts.testFunc, "chol")) {
|
||||
linearSolverCHOL(handle, rowsA, d_A, lda, d_b, d_x);
|
||||
}
|
||||
else if (0 == strcmp(opts.testFunc, "lu")) {
|
||||
linearSolverLU(handle, rowsA, d_A, lda, d_b, d_x);
|
||||
}
|
||||
else if (0 == strcmp(opts.testFunc, "qr")) {
|
||||
linearSolverQR(handle, rowsA, d_A, lda, d_b, d_x);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: %s is unknown function\n", opts.testFunc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
printf("step 6: evaluate residual\n");
|
||||
checkCudaErrors(cudaMemcpy(d_r, d_b, sizeof(double) * rowsA, cudaMemcpyDeviceToDevice));
|
||||
|
||||
// r = b - A*x
|
||||
checkCudaErrors(cublasDgemm_v2(
|
||||
cublasHandle, CUBLAS_OP_N, CUBLAS_OP_N, rowsA, 1, colsA, &minus_one, d_A, lda, d_x, rowsA, &one, d_r, rowsA));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(h_x, d_x, sizeof(double) * colsA, cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaMemcpy(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost));
|
||||
|
||||
x_inf = vec_norminf(colsA, h_x);
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
A_inf = mat_norminf(rowsA, colsA, h_A, lda);
|
||||
|
||||
printf("|b - A*x| = %E \n", r_inf);
|
||||
printf("|A| = %E \n", A_inf);
|
||||
printf("|x| = %E \n", x_inf);
|
||||
printf("|b - A*x|/(|A|*|x|) = %E \n", r_inf / (A_inf * x_inf));
|
||||
|
||||
if (handle) {
|
||||
checkCudaErrors(cusolverDnDestroy(handle));
|
||||
}
|
||||
if (cublasHandle) {
|
||||
checkCudaErrors(cublasDestroy(cublasHandle));
|
||||
}
|
||||
if (stream) {
|
||||
checkCudaErrors(cudaStreamDestroy(stream));
|
||||
}
|
||||
|
||||
if (h_csrValA) {
|
||||
free(h_csrValA);
|
||||
}
|
||||
if (h_csrRowPtrA) {
|
||||
free(h_csrRowPtrA);
|
||||
}
|
||||
if (h_csrColIndA) {
|
||||
free(h_csrColIndA);
|
||||
}
|
||||
|
||||
if (h_A) {
|
||||
free(h_A);
|
||||
}
|
||||
if (h_x) {
|
||||
free(h_x);
|
||||
}
|
||||
if (h_b) {
|
||||
free(h_b);
|
||||
}
|
||||
if (h_r) {
|
||||
free(h_r);
|
||||
}
|
||||
|
||||
if (d_A) {
|
||||
checkCudaErrors(cudaFree(d_A));
|
||||
}
|
||||
if (d_x) {
|
||||
checkCudaErrors(cudaFree(d_x));
|
||||
}
|
||||
if (d_b) {
|
||||
checkCudaErrors(cudaFree(d_b));
|
||||
}
|
||||
if (d_r) {
|
||||
checkCudaErrors(cudaFree(d_r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
4330
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/gr_900_900_crg.mtx
Normal file
4330
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/gr_900_900_crg.mtx
Normal file
File diff suppressed because it is too large
Load Diff
30803
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/lap3D_7pt_n20.mtx
Normal file
30803
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/lap3D_7pt_n20.mtx
Normal file
File diff suppressed because it is too large
Load Diff
481
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/mmio.c
Normal file
481
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/mmio.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
// System includes
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Project includes
|
||||
#include "mmio.h"
|
||||
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_)
|
||||
{
|
||||
FILE *f;
|
||||
MM_typecode matcode;
|
||||
int M, N, nz;
|
||||
int i;
|
||||
double *val;
|
||||
int *I, *J;
|
||||
|
||||
if ((f = fopen(fname, "r")) == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
if (mm_read_banner(f, &matcode) != 0) {
|
||||
printf("mm_read_unsymetric: Could not process Matrix Market banner ");
|
||||
printf(" in file [%s]\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!(mm_is_real(matcode) && mm_is_matrix(matcode) && mm_is_sparse(matcode))) {
|
||||
fprintf(stderr, "Sorry, this application does not support ");
|
||||
fprintf(stderr, "Market Market type: [%s]\n", mm_typecode_to_str(matcode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find out size of sparse matrix: M, N, nz .... */
|
||||
|
||||
if (mm_read_mtx_crd_size(f, &M, &N, &nz) != 0) {
|
||||
fprintf(stderr, "read_unsymmetric_sparse(): could not parse matrix size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*M_ = M;
|
||||
*N_ = N;
|
||||
*nz_ = nz;
|
||||
|
||||
/* reseve memory for matrices */
|
||||
|
||||
I = (int *)malloc(nz * sizeof(int));
|
||||
J = (int *)malloc(nz * sizeof(int));
|
||||
val = (double *)malloc(nz * sizeof(double));
|
||||
|
||||
*val_ = val;
|
||||
*I_ = I;
|
||||
*J_ = J;
|
||||
|
||||
/* NOTE: when reading in doubles, ANSI C requires the use of the "l" */
|
||||
/* specifier as in "%lg", "%lf", "%le", otherwise errors will occur */
|
||||
/* (ANSI C X3.159-1989, Sec. 4.9.6.2, p. 136 lines 13-15) */
|
||||
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3) {
|
||||
return -1;
|
||||
}
|
||||
I[i]--; /* adjust from 1-based to 0-based */
|
||||
J[i]--;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_is_valid(MM_typecode matcode)
|
||||
{
|
||||
if (!mm_is_matrix(matcode))
|
||||
return 0;
|
||||
if (mm_is_dense(matcode) && mm_is_pattern(matcode))
|
||||
return 0;
|
||||
if (mm_is_real(matcode) && mm_is_hermitian(matcode))
|
||||
return 0;
|
||||
if (mm_is_pattern(matcode) && (mm_is_hermitian(matcode) || mm_is_skew(matcode)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
char banner[MM_MAX_TOKEN_LENGTH];
|
||||
char mtx[MM_MAX_TOKEN_LENGTH];
|
||||
char crd[MM_MAX_TOKEN_LENGTH];
|
||||
char data_type[MM_MAX_TOKEN_LENGTH];
|
||||
char storage_scheme[MM_MAX_TOKEN_LENGTH];
|
||||
char *p;
|
||||
|
||||
|
||||
mm_clear_typecode(matcode);
|
||||
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type, storage_scheme) != 5)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
for (p = mtx; *p != '\0'; *p = tolower(*p), p++)
|
||||
; /* convert to lower case */
|
||||
for (p = crd; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = data_type; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = storage_scheme; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
|
||||
/* check for banner */
|
||||
if (strncmp(banner, MatrixMarketBanner, strlen(MatrixMarketBanner)) != 0)
|
||||
return MM_NO_HEADER;
|
||||
|
||||
/* first field should be "mtx" */
|
||||
if (strcmp(mtx, MM_MTX_STR) != 0)
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
mm_set_matrix(matcode);
|
||||
|
||||
|
||||
/* second field describes whether this is a sparse matrix (in coordinate
|
||||
storgae) or a dense array */
|
||||
|
||||
|
||||
if (strcmp(crd, MM_SPARSE_STR) == 0)
|
||||
mm_set_sparse(matcode);
|
||||
else if (strcmp(crd, MM_DENSE_STR) == 0)
|
||||
mm_set_dense(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
/* third field */
|
||||
|
||||
if (strcmp(data_type, MM_REAL_STR) == 0)
|
||||
mm_set_real(matcode);
|
||||
else if (strcmp(data_type, MM_COMPLEX_STR) == 0)
|
||||
mm_set_complex(matcode);
|
||||
else if (strcmp(data_type, MM_PATTERN_STR) == 0)
|
||||
mm_set_pattern(matcode);
|
||||
else if (strcmp(data_type, MM_INT_STR) == 0)
|
||||
mm_set_integer(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
/* fourth field */
|
||||
|
||||
if (strcmp(storage_scheme, MM_GENERAL_STR) == 0)
|
||||
mm_set_general(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SYMM_STR) == 0)
|
||||
mm_set_symmetric(matcode);
|
||||
else if (strcmp(storage_scheme, MM_HERM_STR) == 0)
|
||||
mm_set_hermitian(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SKEW_STR) == 0)
|
||||
mm_set_skew(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz)
|
||||
{
|
||||
if (fprintf(f, "%d %d %d\n", M, N, nz) != 3)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = *nz = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d %d", M, N, nz) == 3)
|
||||
return 0;
|
||||
|
||||
else
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d %d", M, N, nz);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d", M, N) == 2)
|
||||
return 0;
|
||||
|
||||
else /* we have a blank line */
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d", M, N);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N)
|
||||
{
|
||||
if (fprintf(f, "%d %d\n", M, N) != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/******************************************************************/
|
||||
/* use when I[], J[], and val[]J, and val[] are already allocated */
|
||||
/******************************************************************/
|
||||
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
int i;
|
||||
if (mm_is_complex(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d %lg %lg", &I[i], &J[i], &val[2 * i], &val[2 * i + 1]) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d", &I[i], &J[i]) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *imag, MM_typecode matcode)
|
||||
{
|
||||
if (mm_is_complex(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg %lg", I, J, real, imag) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg\n", I, J, real) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
if (fscanf(f, "%d %d", I, J) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
mm_read_mtx_crd() fills M, N, nz, array of values, and return
|
||||
type code, e.g. 'MCRS'
|
||||
|
||||
if matrix is complex, values[] is of size 2*nz,
|
||||
(nz pairs of real/imaginary values)
|
||||
************************************************************************/
|
||||
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode)
|
||||
{
|
||||
int ret_code;
|
||||
FILE *f;
|
||||
|
||||
if (strcmp(fname, "stdin") == 0)
|
||||
f = stdin;
|
||||
else if ((f = fopen(fname, "r")) == NULL)
|
||||
return MM_COULD_NOT_READ_FILE;
|
||||
|
||||
|
||||
if ((ret_code = mm_read_banner(f, matcode)) != 0)
|
||||
return ret_code;
|
||||
|
||||
if (!(mm_is_valid(*matcode) && mm_is_sparse(*matcode) && mm_is_matrix(*matcode)))
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
if ((ret_code = mm_read_mtx_crd_size(f, M, N, nz)) != 0)
|
||||
return ret_code;
|
||||
|
||||
|
||||
*I = (int *)malloc(*nz * sizeof(int));
|
||||
*J = (int *)malloc(*nz * sizeof(int));
|
||||
*val = NULL;
|
||||
|
||||
if (mm_is_complex(*matcode)) {
|
||||
*val = (double *)malloc(*nz * 2 * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
else if (mm_is_real(*matcode) || mm_is_integer(*matcode)) {
|
||||
*val = (double *)malloc(*nz * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(*matcode)) {
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
if (f != stdin)
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode)
|
||||
{
|
||||
char *str = mm_typecode_to_str(matcode);
|
||||
int ret_code;
|
||||
|
||||
ret_code = fprintf(f, "%s %s\n", MatrixMarketBanner, str);
|
||||
free(str);
|
||||
if (ret_code != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
if (strcmp(fname, "stdout") == 0)
|
||||
f = stdout;
|
||||
else if ((f = fopen(fname, "w")) == NULL)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
|
||||
/* print banner followed by typecode */
|
||||
fprintf(f, "%s ", MatrixMarketBanner);
|
||||
fprintf(f, "%s\n", mm_typecode_to_str(matcode));
|
||||
|
||||
/* print matrix sizes and nonzeros */
|
||||
fprintf(f, "%d %d %d\n", M, N, nz);
|
||||
|
||||
/* print values */
|
||||
if (mm_is_pattern(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d\n", I[i], J[i]);
|
||||
else if (mm_is_integer(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %d\n", I[i], J[i], (int)val[i]);
|
||||
else if (mm_is_real(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g\n", I[i], J[i], val[i]);
|
||||
else if (mm_is_complex(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g %20.16g\n", I[i], J[i], val[2 * i], val[2 * i + 1]);
|
||||
else {
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new copy of a string s. mm_strdup() is a common routine, but
|
||||
* not part of ANSI C, so it is included here. Used by mm_typecode_to_str().
|
||||
*
|
||||
*/
|
||||
static char *mm_strdup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *s2 = (char *)malloc((len + 1) * sizeof(char));
|
||||
return strcpy(s2, s);
|
||||
}
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode)
|
||||
{
|
||||
char buffer[MM_MAX_LINE_LENGTH];
|
||||
char *types[4];
|
||||
// char *mm_strdup(const char *);
|
||||
// int error =0;
|
||||
|
||||
/* check for MTX type */
|
||||
if (mm_is_matrix(matcode))
|
||||
types[0] = MM_MTX_STR;
|
||||
else
|
||||
return NULL; // error=1;
|
||||
|
||||
/* check for CRD or ARR matrix */
|
||||
if (mm_is_sparse(matcode))
|
||||
types[1] = MM_SPARSE_STR;
|
||||
else if (mm_is_dense(matcode))
|
||||
types[1] = MM_DENSE_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* check for element data type */
|
||||
if (mm_is_real(matcode))
|
||||
types[2] = MM_REAL_STR;
|
||||
else if (mm_is_complex(matcode))
|
||||
types[2] = MM_COMPLEX_STR;
|
||||
else if (mm_is_pattern(matcode))
|
||||
types[2] = MM_PATTERN_STR;
|
||||
else if (mm_is_integer(matcode))
|
||||
types[2] = MM_INT_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
|
||||
/* check for symmetry type */
|
||||
if (mm_is_general(matcode))
|
||||
types[3] = MM_GENERAL_STR;
|
||||
else if (mm_is_symmetric(matcode))
|
||||
types[3] = MM_SYMM_STR;
|
||||
else if (mm_is_hermitian(matcode))
|
||||
types[3] = MM_HERM_STR;
|
||||
else if (mm_is_skew(matcode))
|
||||
types[3] = MM_SKEW_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
sprintf(buffer, "%s %s %s %s", types[0], types[1], types[2], types[3]);
|
||||
return mm_strdup(buffer);
|
||||
}
|
||||
138
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/mmio.h
Normal file
138
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/mmio.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MM_IO_H
|
||||
#define MM_IO_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define MM_MAX_LINE_LENGTH 1025
|
||||
#define MatrixMarketBanner "%%MatrixMarket"
|
||||
#define MM_MAX_TOKEN_LENGTH 64
|
||||
|
||||
typedef char MM_typecode[4];
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode);
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode);
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz);
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N);
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode);
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz);
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N);
|
||||
|
||||
|
||||
/********************* MM_typecode query fucntions ***************************/
|
||||
|
||||
#define mm_is_matrix(typecode) ((typecode)[0] == 'M')
|
||||
|
||||
#define mm_is_sparse(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_coordinate(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_dense(typecode) ((typecode)[1] == 'A')
|
||||
#define mm_is_array(typecode) ((typecode)[1] == 'A')
|
||||
|
||||
#define mm_is_complex(typecode) ((typecode)[2] == 'C')
|
||||
#define mm_is_real(typecode) ((typecode)[2] == 'R')
|
||||
#define mm_is_pattern(typecode) ((typecode)[2] == 'P')
|
||||
#define mm_is_integer(typecode) ((typecode)[2] == 'I')
|
||||
|
||||
#define mm_is_symmetric(typecode) ((typecode)[3] == 'S')
|
||||
#define mm_is_general(typecode) ((typecode)[3] == 'G')
|
||||
#define mm_is_skew(typecode) ((typecode)[3] == 'K')
|
||||
#define mm_is_hermitian(typecode) ((typecode)[3] == 'H')
|
||||
|
||||
int mm_is_valid(MM_typecode matcode); /* too complex for a macro */
|
||||
|
||||
|
||||
/********************* MM_typecode modify fucntions ***************************/
|
||||
|
||||
#define mm_set_matrix(typecode) ((*typecode)[0] = 'M')
|
||||
#define mm_set_coordinate(typecode) ((*typecode)[1] = 'C')
|
||||
#define mm_set_array(typecode) ((*typecode)[1] = 'A')
|
||||
#define mm_set_dense(typecode) mm_set_array(typecode)
|
||||
#define mm_set_sparse(typecode) mm_set_coordinate(typecode)
|
||||
|
||||
#define mm_set_complex(typecode) ((*typecode)[2] = 'C')
|
||||
#define mm_set_real(typecode) ((*typecode)[2] = 'R')
|
||||
#define mm_set_pattern(typecode) ((*typecode)[2] = 'P')
|
||||
#define mm_set_integer(typecode) ((*typecode)[2] = 'I')
|
||||
|
||||
|
||||
#define mm_set_symmetric(typecode) ((*typecode)[3] = 'S')
|
||||
#define mm_set_general(typecode) ((*typecode)[3] = 'G')
|
||||
#define mm_set_skew(typecode) ((*typecode)[3] = 'K')
|
||||
#define mm_set_hermitian(typecode) ((*typecode)[3] = 'H')
|
||||
|
||||
#define mm_clear_typecode(typecode) ((*typecode)[0] = (*typecode)[1] = (*typecode)[2] = ' ', (*typecode)[3] = 'G')
|
||||
|
||||
#define mm_initialize_typecode(typecode) mm_clear_typecode(typecode)
|
||||
|
||||
|
||||
/********************* Matrix Market error codes ***************************/
|
||||
|
||||
|
||||
#define MM_COULD_NOT_READ_FILE 11
|
||||
#define MM_PREMATURE_EOF 12
|
||||
#define MM_NOT_MTX 13
|
||||
#define MM_NO_HEADER 14
|
||||
#define MM_UNSUPPORTED_TYPE 15
|
||||
#define MM_LINE_TOO_LONG 16
|
||||
#define MM_COULD_NOT_WRITE_FILE 17
|
||||
|
||||
|
||||
/******************** Matrix Market internal definitions ********************
|
||||
|
||||
MM_matrix_typecode: 4-character sequence
|
||||
|
||||
ojbect sparse/ data storage
|
||||
dense type scheme
|
||||
|
||||
string position: [0] [1] [2] [3]
|
||||
|
||||
Matrix typecode: M(atrix) C(oord) R(eal) G(eneral)
|
||||
A(array) C(omplex) H(ermitian)
|
||||
P(attern) S(ymmetric)
|
||||
I(nteger) K(kew)
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#define MM_MTX_STR "matrix"
|
||||
#define MM_ARRAY_STR "array"
|
||||
#define MM_DENSE_STR "array"
|
||||
#define MM_COORDINATE_STR "coordinate"
|
||||
#define MM_SPARSE_STR "coordinate"
|
||||
#define MM_COMPLEX_STR "complex"
|
||||
#define MM_REAL_STR "real"
|
||||
#define MM_INT_STR "integer"
|
||||
#define MM_GENERAL_STR "general"
|
||||
#define MM_SYMM_STR "symmetric"
|
||||
#define MM_HERM_STR "hermitian"
|
||||
#define MM_SKEW_STR "skew-symmetric"
|
||||
#define MM_PATTERN_STR "pattern"
|
||||
|
||||
|
||||
/* high level routines */
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode);
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *img, MM_typecode matcode);
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
473
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/mmio_wrapper.cpp
Normal file
473
cpp/4_CUDA_Libraries/cuSolverDn_LinearSolver/mmio_wrapper.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
|
||||
#include <cusolverDn.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
/* various __inline__ __device__ function to initialize a T_ELEM */
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(int);
|
||||
template <> __inline__ float cuGet<float>(int x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(int x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(int x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(int x) { return (make_cuDoubleComplex(double(x), 0.0)); }
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(int, int);
|
||||
template <> __inline__ float cuGet<float>(int x, int y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(int x, int y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(int x, int y) { return make_cuComplex(float(x), float(y)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(int x, int y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(float);
|
||||
template <> __inline__ float cuGet<float>(float x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(float x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(float x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(float x)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), 0.0));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(float, float);
|
||||
template <> __inline__ float cuGet<float>(float x, float y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(float x, float y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(float x, float y) { return (make_cuComplex(float(x), float(y))); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(float x, float y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(double);
|
||||
template <> __inline__ float cuGet<float>(double x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(double x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(double x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(double x)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), 0.0));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(double, double);
|
||||
template <> __inline__ float cuGet<float>(double x, double y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(double x, double y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(double x, double y) { return (make_cuComplex(float(x), float(y))); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(double x, double y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
|
||||
static void compress_index(const int *Ind, int nnz, int m, int *Ptr, int base)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* initialize everything to zero */
|
||||
for (i = 0; i < m + 1; i++) {
|
||||
Ptr[i] = 0;
|
||||
}
|
||||
/* count elements in every row */
|
||||
Ptr[0] = base;
|
||||
for (i = 0; i < nnz; i++) {
|
||||
Ptr[Ind[i] + (1 - base)]++;
|
||||
}
|
||||
/* add all the values */
|
||||
for (i = 0; i < m; i++) {
|
||||
Ptr[i + 1] += Ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct cooFormat
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int p; // permutation
|
||||
};
|
||||
|
||||
|
||||
int cmp_cooFormat_csr(struct cooFormat *s, struct cooFormat *t)
|
||||
{
|
||||
if (s->i < t->i) {
|
||||
return -1;
|
||||
}
|
||||
else if (s->i > t->i) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return s->j - t->j;
|
||||
}
|
||||
}
|
||||
|
||||
int cmp_cooFormat_csc(struct cooFormat *s, struct cooFormat *t)
|
||||
{
|
||||
if (s->j < t->j) {
|
||||
return -1;
|
||||
}
|
||||
else if (s->j > t->j) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return s->i - t->i;
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*FUNPTR)(const void *, const void *);
|
||||
typedef int (*FUNPTR2)(struct cooFormat *s, struct cooFormat *t);
|
||||
|
||||
static FUNPTR2 fptr_array[2] = {
|
||||
cmp_cooFormat_csr,
|
||||
cmp_cooFormat_csc,
|
||||
};
|
||||
|
||||
|
||||
static int verify_pattern(int m, int nnz, int *csrRowPtr, int *csrColInd)
|
||||
{
|
||||
int i, col, start, end, base_index;
|
||||
int error_found = 0;
|
||||
|
||||
if (nnz != (csrRowPtr[m] - csrRowPtr[0])) {
|
||||
fprintf(stderr,
|
||||
"Error (nnz check failed): (csrRowPtr[%d]=%d - csrRowPtr[%d]=%d) != (nnz=%d)\n",
|
||||
0,
|
||||
csrRowPtr[0],
|
||||
m,
|
||||
csrRowPtr[m],
|
||||
nnz);
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
base_index = csrRowPtr[0];
|
||||
if ((0 != base_index) && (1 != base_index)) {
|
||||
fprintf(stderr, "Error (base index check failed): base index = %d\n", base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
for (i = 0; (!error_found) && (i < m); i++) {
|
||||
start = csrRowPtr[i] - base_index;
|
||||
end = csrRowPtr[i + 1] - base_index;
|
||||
if (start > end) {
|
||||
fprintf(stderr,
|
||||
"Error (corrupted row): csrRowPtr[%d] (=%d) > csrRowPtr[%d] (=%d)\n",
|
||||
i,
|
||||
start + base_index,
|
||||
i + 1,
|
||||
end + base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
for (col = start; col < end; col++) {
|
||||
if (csrColInd[col] < base_index) {
|
||||
fprintf(stderr, "Error (column vs. base index check failed): csrColInd[%d] < %d\n", col, base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
if ((col < (end - 1)) && (csrColInd[col] >= csrColInd[col + 1])) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error (sorting of the column indecis check failed): (csrColInd[%d]=%d) >= (csrColInd[%d]=%d)\n",
|
||||
col,
|
||||
csrColInd[col],
|
||||
col + 1,
|
||||
csrColInd[col + 1]);
|
||||
error_found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error_found;
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix)
|
||||
{
|
||||
MM_typecode matcode;
|
||||
double *tempVal;
|
||||
int *tempRowInd, *tempColInd;
|
||||
double *tval;
|
||||
int *trow, *tcol;
|
||||
int *csrRowPtr, *cscColPtr;
|
||||
int i, j, error, base, count;
|
||||
struct cooFormat *work;
|
||||
|
||||
/* read the matrix */
|
||||
error = mm_read_mtx_crd(filename, m, n, nnz, &trow, &tcol, &tval, &matcode);
|
||||
if (error) {
|
||||
fprintf(stderr, "!!!! can not open file: '%s'\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start error checking */
|
||||
if (mm_is_complex(matcode) && ((elem_type != 'z') && (elem_type != 'c'))) {
|
||||
fprintf(stderr, "!!!! complex matrix requires type 'z' or 'c'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mm_is_dense(matcode) || mm_is_array(matcode) || mm_is_pattern(matcode) /*|| mm_is_integer(matcode)*/) {
|
||||
fprintf(stderr, "!!!! dense, array, pattern and integer matrices are not supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if necessary symmetrize the pattern (transform from triangular to full) */
|
||||
if ((extendSymMatrix) && (mm_is_symmetric(matcode) || mm_is_hermitian(matcode) || mm_is_skew(matcode))) {
|
||||
// count number of non-diagonal elements
|
||||
count = 0;
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
if (trow[i] != tcol[i]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// allocate space for the symmetrized matrix
|
||||
tempRowInd = (int *)malloc((*nnz + count) * sizeof(int));
|
||||
tempColInd = (int *)malloc((*nnz + count) * sizeof(int));
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
tempVal = (double *)malloc((*nnz + count) * sizeof(double));
|
||||
}
|
||||
else {
|
||||
tempVal = (double *)malloc(2 * (*nnz + count) * sizeof(double));
|
||||
}
|
||||
// copy the elements regular and transposed locations
|
||||
for (j = 0, i = 0; i < (*nnz); i++) {
|
||||
tempRowInd[j] = trow[i];
|
||||
tempColInd[j] = tcol[i];
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
tempVal[j] = tval[i];
|
||||
}
|
||||
else {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = tval[2 * i + 1];
|
||||
}
|
||||
j++;
|
||||
if (trow[i] != tcol[i]) {
|
||||
tempRowInd[j] = tcol[i];
|
||||
tempColInd[j] = trow[i];
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (mm_is_skew(matcode)) {
|
||||
tempVal[j] = -tval[i];
|
||||
}
|
||||
else {
|
||||
tempVal[j] = tval[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mm_is_hermitian(matcode)) {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = -tval[2 * i + 1];
|
||||
}
|
||||
else {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = tval[2 * i + 1];
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
(*nnz) += count;
|
||||
// free temporary storage
|
||||
free(trow);
|
||||
free(tcol);
|
||||
free(tval);
|
||||
}
|
||||
else {
|
||||
tempRowInd = trow;
|
||||
tempColInd = tcol;
|
||||
tempVal = tval;
|
||||
}
|
||||
// life time of (trow, tcol, tval) is over.
|
||||
// please use COO format (tempRowInd, tempColInd, tempVal)
|
||||
|
||||
// use qsort to sort COO format
|
||||
work = (struct cooFormat *)malloc(sizeof(struct cooFormat) * (*nnz));
|
||||
if (NULL == work) {
|
||||
fprintf(stderr, "!!!! allocation error, malloc failed\n");
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
work[i].i = tempRowInd[i];
|
||||
work[i].j = tempColInd[i];
|
||||
work[i].p = i; // permutation is identity
|
||||
}
|
||||
|
||||
if (csrFormat) {
|
||||
/* create row-major ordering of indices (sorted by row and within each row by column) */
|
||||
qsort(work, *nnz, sizeof(struct cooFormat), (FUNPTR)fptr_array[0]);
|
||||
}
|
||||
else {
|
||||
/* create column-major ordering of indices (sorted by column and within each column by row) */
|
||||
qsort(work, *nnz, sizeof(struct cooFormat), (FUNPTR)fptr_array[1]);
|
||||
}
|
||||
|
||||
// (tempRowInd, tempColInd) is sorted either by row-major or by col-major
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
tempRowInd[i] = work[i].i;
|
||||
tempColInd[i] = work[i].j;
|
||||
}
|
||||
|
||||
// setup base
|
||||
// check if there is any row/col 0, if so base-0
|
||||
// check if there is any row/col equal to matrix dimension m/n, if so base-1
|
||||
int base0 = 0;
|
||||
int base1 = 0;
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
const int row = tempRowInd[i];
|
||||
const int col = tempColInd[i];
|
||||
if ((0 == row) || (0 == col)) {
|
||||
base0 = 1;
|
||||
}
|
||||
if ((*m == row) || (*n == col)) {
|
||||
base1 = 1;
|
||||
}
|
||||
}
|
||||
if (base0 && base1) {
|
||||
printf("Error: input matrix is base-0 and base-1 \n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
base = 0;
|
||||
if (base1) {
|
||||
base = 1;
|
||||
}
|
||||
|
||||
/* compress the appropriate indices */
|
||||
if (csrFormat) {
|
||||
/* CSR format (assuming row-major format) */
|
||||
csrRowPtr = (int *)malloc(((*m) + 1) * sizeof(csrRowPtr[0]));
|
||||
if (!csrRowPtr)
|
||||
return 1;
|
||||
compress_index(tempRowInd, *nnz, *m, csrRowPtr, base);
|
||||
|
||||
*aRowInd = csrRowPtr;
|
||||
*aColInd = (int *)malloc((*nnz) * sizeof(int));
|
||||
}
|
||||
else {
|
||||
/* CSC format (assuming column-major format) */
|
||||
cscColPtr = (int *)malloc(((*n) + 1) * sizeof(cscColPtr[0]));
|
||||
if (!cscColPtr)
|
||||
return 1;
|
||||
compress_index(tempColInd, *nnz, *n, cscColPtr, base);
|
||||
|
||||
*aColInd = cscColPtr;
|
||||
*aRowInd = (int *)malloc((*nnz) * sizeof(int));
|
||||
}
|
||||
|
||||
/* transfrom the matrix values of type double into one of the cusparse library types */
|
||||
*aVal = (T_ELEM *)malloc((*nnz) * sizeof(T_ELEM));
|
||||
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
if (csrFormat) {
|
||||
(*aColInd)[i] = tempColInd[i];
|
||||
}
|
||||
else {
|
||||
(*aRowInd)[i] = tempRowInd[i];
|
||||
}
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
(*aVal)[i] = cuGet<T_ELEM>(tempVal[work[i].p]);
|
||||
}
|
||||
else {
|
||||
(*aVal)[i] = cuGet<T_ELEM>(tempVal[2 * work[i].p], tempVal[2 * work[i].p + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for corruption */
|
||||
int error_found;
|
||||
if (csrFormat) {
|
||||
error_found = verify_pattern(*m, *nnz, *aRowInd, *aColInd);
|
||||
}
|
||||
else {
|
||||
error_found = verify_pattern(*n, *nnz, *aColInd, *aRowInd);
|
||||
}
|
||||
if (error_found) {
|
||||
fprintf(stderr, "!!!! verify_pattern failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cleanup and exit */
|
||||
free(work);
|
||||
free(tempVal);
|
||||
free(tempColInd);
|
||||
free(tempRowInd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* specific instantiation */
|
||||
template int loadMMSparseMatrix<float>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
float **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<double>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
double **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<cuComplex>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
cuComplex **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<cuDoubleComplex>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
cuDoubleComplex **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
18
cpp/4_CUDA_Libraries/cuSolverRf/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/cuSolverRf/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/cuSolverRf/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/cuSolverRf/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
58
cpp/4_CUDA_Libraries/cuSolverRf/CMakeLists.txt
Normal file
58
cpp/4_CUDA_Libraries/cuSolverRf/CMakeLists.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cuSolverRf LANGUAGES C CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cuSolverRf
|
||||
add_executable(cuSolverRf cuSolverRf.cpp mmio.c mmio_wrapper.cpp)
|
||||
|
||||
target_compile_options(cuSolverRf PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cuSolverRf PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cuSolverRf PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(cuSolverRf PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(cuSolverRf PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusolver
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverRf POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap2D_5pt_n100.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverRf POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap3D_7pt_n20.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
39
cpp/4_CUDA_Libraries/cuSolverRf/README.md
Normal file
39
cpp/4_CUDA_Libraries/cuSolverRf/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# cuSolverRf - cuSolverRf Refactorization
|
||||
|
||||
## Description
|
||||
|
||||
A CUDA Sample that demonstrates cuSolver's refactorization library - CUSOLVERRF.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUSOLVER Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuGet, cuDoubleComplex, cuComplex
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaStreamDestroy, cudaFree, cudaDeviceSynchronize, cudaMalloc, cudaStreamCreate
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUSOLVER](../../../README.md#cusolver), [CUBLAS](../../../README.md#cublas), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
899
cpp/4_CUDA_Libraries/cuSolverRf/cuSolverRf.cpp
Normal file
899
cpp/4_CUDA_Libraries/cuSolverRf/cuSolverRf.cpp
Normal file
@@ -0,0 +1,899 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A framework of refactorization process.
|
||||
*
|
||||
* step 1: compute P*A*Q = L*U by
|
||||
* - reordering and
|
||||
* - LU with partial pivoting in cusolverSp
|
||||
*
|
||||
* step 2: set up cusolverRf by (P, Q, L, U)
|
||||
*
|
||||
* step 3: analyze and refactor A
|
||||
*
|
||||
* How to use
|
||||
* ./cuSolverRf -P=symrcm -file <file>
|
||||
* ./cuSolverRf -P=symamd -file <file>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cusolverRf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cusolverSp.h"
|
||||
#include "cusolverSp_LOWLEVEL_PREVIEW.h"
|
||||
#include "helper_cuda.h"
|
||||
#include "helper_cusolver.h"
|
||||
#include "helper_string.h"
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
void UsageRF(void)
|
||||
{
|
||||
printf("<options>\n");
|
||||
printf("-h : display this help\n");
|
||||
printf("-P=<name> : choose a reordering\n");
|
||||
printf(" symrcm (Reverse Cuthill-McKee)\n");
|
||||
printf(" symamd (Approximate Minimum Degree)\n");
|
||||
printf("-file=<filename> : filename containing a matrix in MM format\n");
|
||||
printf("-device=<device_id> : <device_id> if want to run on specific GPU\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void parseCommandLineArguments(int argc, char *argv[], struct testOpts &opts)
|
||||
{
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "-h")) {
|
||||
UsageRF();
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "P")) {
|
||||
char *reorderType = NULL;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "P", &reorderType);
|
||||
|
||||
if (reorderType) {
|
||||
if ((STRCASECMP(reorderType, "symrcm") != 0) && (STRCASECMP(reorderType, "symamd") != 0)) {
|
||||
printf("\nIncorrect argument passed to -P option\n");
|
||||
UsageRF();
|
||||
}
|
||||
else {
|
||||
opts.reorder = reorderType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts.reorder) {
|
||||
opts.reorder = "symrcm"; // Setting default reordering to be symrcm.
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
|
||||
char *fileName = 0;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "file", &fileName);
|
||||
|
||||
if (fileName) {
|
||||
opts.sparse_mat_filename = fileName;
|
||||
}
|
||||
else {
|
||||
printf("\nIncorrect filename passed to -file \n ");
|
||||
UsageRF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct testOpts opts;
|
||||
cusolverRfHandle_t cusolverRfH = NULL; // refactorization
|
||||
cusolverSpHandle_t cusolverSpH = NULL; // reordering, permutation and 1st LU factorization
|
||||
cusparseHandle_t cusparseH = NULL; // residual evaluation
|
||||
cudaStream_t stream = NULL;
|
||||
cusparseMatDescr_t descrA = NULL; // A is a base-0 general matrix
|
||||
|
||||
csrluInfoHost_t info = NULL; // opaque info structure for LU with parital pivoting
|
||||
|
||||
int rowsA = 0; // number of rows of A
|
||||
int colsA = 0; // number of columns of A
|
||||
int nnzA = 0; // number of nonzeros of A
|
||||
int baseA = 0; // base index in CSR format
|
||||
// cusolverRf only works for base-0
|
||||
|
||||
// cusolverRf only works for square matrix,
|
||||
// assume n = rowsA = colsA
|
||||
|
||||
// CSR(A) from I/O
|
||||
int *h_csrRowPtrA = NULL; // <int> n+1
|
||||
int *h_csrColIndA = NULL; // <int> nnzA
|
||||
double *h_csrValA = NULL; // <double> nnzA
|
||||
|
||||
int *h_Qreorder = NULL; // <int> n
|
||||
// reorder to reduce zero fill-in
|
||||
// Qreorder = symrcm(A) or Qreroder = symamd(A)
|
||||
// B = Q*A*Q^T
|
||||
int *h_csrRowPtrB = NULL; // <int> n+1
|
||||
int *h_csrColIndB = NULL; // <int> nnzA
|
||||
double *h_csrValB = NULL; // <double> nnzA
|
||||
int *h_mapBfromA = NULL; // <int> nnzA
|
||||
|
||||
double *h_x = NULL; // <double> n, x = A \ b
|
||||
double *h_b = NULL; // <double> n, b = ones(m,1)
|
||||
double *h_r = NULL; // <double> n, r = b - A*x
|
||||
|
||||
// solve B*(Qx) = Q*b
|
||||
double *h_xhat = NULL; // <double> n, Q*x_hat = x
|
||||
double *h_bhat = NULL; // <double> n, b_hat = Q*b
|
||||
|
||||
size_t size_perm = 0;
|
||||
size_t size_internal = 0;
|
||||
size_t size_lu = 0; // size of working space for csrlu
|
||||
void *buffer_cpu = NULL; // working space for
|
||||
// - permutation: B = Q*A*Q^T
|
||||
// - LU with partial pivoting in cusolverSp
|
||||
|
||||
// cusolverSp computes LU with partial pivoting
|
||||
// Plu*B*Qlu^T = L*U
|
||||
// where B = Q*A*Q^T
|
||||
//
|
||||
// nnzL and nnzU are not known until factorization is done.
|
||||
// However upper bound of L+U is known after symbolic analysis of LU.
|
||||
int *h_Plu = NULL; // <int> n
|
||||
int *h_Qlu = NULL; // <int> n
|
||||
|
||||
int nnzL = 0;
|
||||
int *h_csrRowPtrL = NULL; // <int> n+1
|
||||
int *h_csrColIndL = NULL; // <int> nnzL
|
||||
double *h_csrValL = NULL; // <double> nnzL
|
||||
|
||||
int nnzU = 0;
|
||||
int *h_csrRowPtrU = NULL; // <int> n+1
|
||||
int *h_csrColIndU = NULL; // <int> nnzU
|
||||
double *h_csrValU = NULL; // <double> nnzU
|
||||
|
||||
int *h_P = NULL; // <int> n, P = Plu * Qreorder
|
||||
int *h_Q = NULL; // <int> n, Q = Qlu * Qreorder
|
||||
|
||||
int *d_csrRowPtrA = NULL; // <int> n+1
|
||||
int *d_csrColIndA = NULL; // <int> nnzA
|
||||
double *d_csrValA = NULL; // <double> nnzA
|
||||
double *d_x = NULL; // <double> n, x = A \ b
|
||||
double *d_b = NULL; // <double> n, a copy of h_b
|
||||
double *d_r = NULL; // <double> n, r = b - A*x
|
||||
|
||||
int *d_P = NULL; // <int> n, P*A*Q^T = L*U
|
||||
int *d_Q = NULL; // <int> n
|
||||
|
||||
double *d_T = NULL; // working space in cusolverRfSolve
|
||||
// |d_T| = n * nrhs
|
||||
|
||||
// the constants used in residual evaluation, r = b - A*x
|
||||
const double minus_one = -1.0;
|
||||
const double one = 1.0;
|
||||
// the constants used in cusolverRf
|
||||
// nzero is the value below which zero pivot is flagged.
|
||||
// nboost is the value which is substitured for zero pivot.
|
||||
double nzero = 0.0;
|
||||
double nboost = 0.0;
|
||||
// the constant used in cusolverSp
|
||||
// singularity is -1 if A is invertible under tol
|
||||
// tol determines the condition of singularity
|
||||
// pivot_threshold decides pivoting strategy
|
||||
int singularity = 0;
|
||||
const double tol = 1.e-14;
|
||||
const double pivot_threshold = 1.0;
|
||||
// the constants used in cusolverRf
|
||||
const cusolverRfFactorization_t fact_alg = CUSOLVERRF_FACTORIZATION_ALG0; // default
|
||||
const cusolverRfTriangularSolve_t solve_alg = CUSOLVERRF_TRIANGULAR_SOLVE_ALG1; // default
|
||||
|
||||
double x_inf = 0.0; // |x|
|
||||
double r_inf = 0.0; // |r|
|
||||
double A_inf = 0.0; // |A|
|
||||
int errors = 0;
|
||||
|
||||
double start, stop;
|
||||
double time_reorder;
|
||||
double time_perm;
|
||||
double time_sp_analysis;
|
||||
double time_sp_factor;
|
||||
double time_sp_solve;
|
||||
double time_sp_extract;
|
||||
double time_rf_assemble;
|
||||
double time_rf_reset;
|
||||
double time_rf_refactor;
|
||||
double time_rf_solve;
|
||||
|
||||
parseCommandLineArguments(argc, argv, opts);
|
||||
|
||||
printf("step 1.1: preparation\n");
|
||||
printf("step 1.1: read matrix market format\n");
|
||||
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (opts.sparse_mat_filename == NULL) {
|
||||
opts.sparse_mat_filename = sdkFindFilePath("lap2D_5pt_n100.mtx", argv[0]);
|
||||
printf("Using default input file [%s]\n", opts.sparse_mat_filename);
|
||||
}
|
||||
else {
|
||||
printf("Using input file [%s]\n", opts.sparse_mat_filename);
|
||||
}
|
||||
|
||||
if (opts.sparse_mat_filename) {
|
||||
if (loadMMSparseMatrix<double>(opts.sparse_mat_filename,
|
||||
'd',
|
||||
true,
|
||||
&rowsA,
|
||||
&colsA,
|
||||
&nnzA,
|
||||
&h_csrValA,
|
||||
&h_csrRowPtrA,
|
||||
&h_csrColIndA,
|
||||
true)) {
|
||||
return 1;
|
||||
}
|
||||
baseA = h_csrRowPtrA[0]; // baseA = {0,1}
|
||||
}
|
||||
|
||||
if (rowsA != colsA) {
|
||||
fprintf(stderr, "Error: only support square matrix\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("WARNING: cusolverRf only works for base-0 \n");
|
||||
if (baseA) {
|
||||
for (int i = 0; i <= rowsA; i++) {
|
||||
h_csrRowPtrA[i]--;
|
||||
}
|
||||
for (int i = 0; i < nnzA; i++) {
|
||||
h_csrColIndA[i]--;
|
||||
}
|
||||
baseA = 0;
|
||||
}
|
||||
|
||||
printf("sparse matrix A is %d x %d with %d nonzeros, base=%d\n", rowsA, colsA, nnzA, baseA);
|
||||
|
||||
checkCudaErrors(cusolverSpCreate(&cusolverSpH));
|
||||
checkCudaErrors(cusparseCreate(&cusparseH));
|
||||
checkCudaErrors(cudaStreamCreate(&stream));
|
||||
|
||||
checkCudaErrors(cusolverSpSetStream(cusolverSpH, stream));
|
||||
checkCudaErrors(cusparseSetStream(cusparseH, stream));
|
||||
|
||||
checkCudaErrors(cusparseCreateMatDescr(&descrA));
|
||||
checkCudaErrors(cusparseSetMatType(descrA, CUSPARSE_MATRIX_TYPE_GENERAL));
|
||||
|
||||
if (baseA) {
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descrA, CUSPARSE_INDEX_BASE_ONE));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descrA, CUSPARSE_INDEX_BASE_ZERO));
|
||||
}
|
||||
|
||||
h_Qreorder = (int *)malloc(sizeof(int) * colsA);
|
||||
|
||||
h_csrRowPtrB = (int *)malloc(sizeof(int) * (rowsA + 1));
|
||||
h_csrColIndB = (int *)malloc(sizeof(int) * nnzA);
|
||||
h_csrValB = (double *)malloc(sizeof(double) * nnzA);
|
||||
h_mapBfromA = (int *)malloc(sizeof(int) * nnzA);
|
||||
|
||||
h_x = (double *)malloc(sizeof(double) * colsA);
|
||||
h_b = (double *)malloc(sizeof(double) * rowsA);
|
||||
h_r = (double *)malloc(sizeof(double) * rowsA);
|
||||
h_xhat = (double *)malloc(sizeof(double) * colsA);
|
||||
h_bhat = (double *)malloc(sizeof(double) * rowsA);
|
||||
|
||||
assert(NULL != h_Qreorder);
|
||||
|
||||
assert(NULL != h_csrRowPtrB);
|
||||
assert(NULL != h_csrColIndB);
|
||||
assert(NULL != h_csrValB);
|
||||
assert(NULL != h_mapBfromA);
|
||||
|
||||
assert(NULL != h_x);
|
||||
assert(NULL != h_b);
|
||||
assert(NULL != h_r);
|
||||
assert(NULL != h_xhat);
|
||||
assert(NULL != h_bhat);
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrRowPtrA, sizeof(int) * (rowsA + 1)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrColIndA, sizeof(int) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrValA, sizeof(double) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, sizeof(double) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_b, sizeof(double) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, sizeof(double) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_P, sizeof(int) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_Q, sizeof(int) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_T, sizeof(double) * rowsA * 1));
|
||||
|
||||
printf("step 1.2: set right hand side vector (b) to 1\n");
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
h_b[row] = 1.0;
|
||||
}
|
||||
|
||||
printf("step 2: reorder the matrix to reduce zero fill-in\n");
|
||||
printf(" Q = symrcm(A) or Q = symamd(A) \n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
if (0 == strcmp(opts.reorder, "symrcm")) {
|
||||
checkCudaErrors(
|
||||
cusolverSpXcsrsymrcmHost(cusolverSpH, rowsA, nnzA, descrA, h_csrRowPtrA, h_csrColIndA, h_Qreorder));
|
||||
}
|
||||
else if (0 == strcmp(opts.reorder, "symamd")) {
|
||||
checkCudaErrors(
|
||||
cusolverSpXcsrsymamdHost(cusolverSpH, rowsA, nnzA, descrA, h_csrRowPtrA, h_csrColIndA, h_Qreorder));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: %s is unknow reordering\n", opts.reorder);
|
||||
return 1;
|
||||
}
|
||||
|
||||
stop = second();
|
||||
time_reorder = stop - start;
|
||||
|
||||
printf("step 3: B = Q*A*Q^T\n");
|
||||
memcpy(h_csrRowPtrB, h_csrRowPtrA, sizeof(int) * (rowsA + 1));
|
||||
memcpy(h_csrColIndB, h_csrColIndA, sizeof(int) * nnzA);
|
||||
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverSpXcsrperm_bufferSizeHost(
|
||||
cusolverSpH, rowsA, colsA, nnzA, descrA, h_csrRowPtrB, h_csrColIndB, h_Qreorder, h_Qreorder, &size_perm));
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
buffer_cpu = (void *)malloc(sizeof(char) * size_perm);
|
||||
assert(NULL != buffer_cpu);
|
||||
|
||||
// h_mapBfromA = Identity
|
||||
for (int j = 0; j < nnzA; j++) {
|
||||
h_mapBfromA[j] = j;
|
||||
}
|
||||
checkCudaErrors(cusolverSpXcsrpermHost(cusolverSpH,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
descrA,
|
||||
h_csrRowPtrB,
|
||||
h_csrColIndB,
|
||||
h_Qreorder,
|
||||
h_Qreorder,
|
||||
h_mapBfromA,
|
||||
buffer_cpu));
|
||||
|
||||
// B = A( mapBfromA )
|
||||
for (int j = 0; j < nnzA; j++) {
|
||||
h_csrValB[j] = h_csrValA[h_mapBfromA[j]];
|
||||
}
|
||||
|
||||
stop = second();
|
||||
time_perm = stop - start;
|
||||
|
||||
printf("step 4: solve A*x = b by LU(B) in cusolverSp\n");
|
||||
|
||||
printf("step 4.1: create opaque info structure\n");
|
||||
checkCudaErrors(cusolverSpCreateCsrluInfoHost(&info));
|
||||
|
||||
printf("step 4.2: analyze LU(B) to know structure of Q and R, and upper bound "
|
||||
"for nnz(L+U)\n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverSpXcsrluAnalysisHost(cusolverSpH, rowsA, nnzA, descrA, h_csrRowPtrB, h_csrColIndB, info));
|
||||
|
||||
stop = second();
|
||||
time_sp_analysis = stop - start;
|
||||
|
||||
printf("step 4.3: workspace for LU(B)\n");
|
||||
checkCudaErrors(cusolverSpDcsrluBufferInfoHost(
|
||||
cusolverSpH, rowsA, nnzA, descrA, h_csrValB, h_csrRowPtrB, h_csrColIndB, info, &size_internal, &size_lu));
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
buffer_cpu = (void *)malloc(sizeof(char) * size_lu);
|
||||
assert(NULL != buffer_cpu);
|
||||
|
||||
printf("step 4.4: compute Ppivot*B = L*U \n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverSpDcsrluFactorHost(
|
||||
cusolverSpH, rowsA, nnzA, descrA, h_csrValB, h_csrRowPtrB, h_csrColIndB, info, pivot_threshold, buffer_cpu));
|
||||
|
||||
stop = second();
|
||||
time_sp_factor = stop - start;
|
||||
|
||||
// TODO: check singularity by tol
|
||||
printf("step 4.5: check if the matrix is singular \n");
|
||||
checkCudaErrors(cusolverSpDcsrluZeroPivotHost(cusolverSpH, info, tol, &singularity));
|
||||
|
||||
if (0 <= singularity) {
|
||||
fprintf(stderr, "Error: A is not invertible, singularity=%d\n", singularity);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("step 4.6: solve A*x = b \n");
|
||||
printf(" i.e. solve B*(Qx) = Q*b \n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
// b_hat = Q*b
|
||||
for (int j = 0; j < rowsA; j++) {
|
||||
h_bhat[j] = h_b[h_Qreorder[j]];
|
||||
}
|
||||
// B*x_hat = b_hat
|
||||
checkCudaErrors(cusolverSpDcsrluSolveHost(cusolverSpH, rowsA, h_bhat, h_xhat, info, buffer_cpu));
|
||||
|
||||
// x = Q^T * x_hat
|
||||
for (int j = 0; j < rowsA; j++) {
|
||||
h_x[h_Qreorder[j]] = h_xhat[j];
|
||||
}
|
||||
|
||||
stop = second();
|
||||
time_sp_solve = stop - start;
|
||||
|
||||
printf("step 4.7: evaluate residual r = b - A*x (result on CPU)\n");
|
||||
// use GPU gemv to compute r = b - A*x
|
||||
checkCudaErrors(cudaMemcpy(d_csrRowPtrA, h_csrRowPtrA, sizeof(int) * (rowsA + 1), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrColIndA, h_csrColIndA, sizeof(int) * nnzA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrValA, h_csrValA, sizeof(double) * nnzA, cudaMemcpyHostToDevice));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(d_r, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_x, h_x, sizeof(double) * colsA, cudaMemcpyHostToDevice));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
if (baseA) {
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
d_csrRowPtrA,
|
||||
d_csrColIndA,
|
||||
d_csrValA,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ONE,
|
||||
CUDA_R_64F));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
d_csrRowPtrA,
|
||||
d_csrColIndA,
|
||||
d_csrValA,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_64F));
|
||||
}
|
||||
|
||||
cusparseDnVecDescr_t vecx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecx, colsA, d_x, CUDA_R_64F));
|
||||
cusparseDnVecDescr_t vecAx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecAx, rowsA, d_r, CUDA_R_64F));
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
size_t bufferSize = 0;
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseH,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSize));
|
||||
void *buffer = NULL;
|
||||
checkCudaErrors(cudaMalloc(&buffer, bufferSize));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseH,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost));
|
||||
|
||||
x_inf = vec_norminf(colsA, h_x);
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
A_inf = csr_mat_norminf(rowsA, colsA, nnzA, descrA, h_csrValA, h_csrRowPtrA, h_csrColIndA);
|
||||
|
||||
printf("(CPU) |b - A*x| = %E \n", r_inf);
|
||||
printf("(CPU) |A| = %E \n", A_inf);
|
||||
printf("(CPU) |x| = %E \n", x_inf);
|
||||
printf("(CPU) |b - A*x|/(|A|*|x|) = %E \n", r_inf / (A_inf * x_inf));
|
||||
|
||||
printf("step 5: extract P, Q, L and U from P*B*Q^T = L*U \n");
|
||||
printf(" L has implicit unit diagonal\n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverSpXcsrluNnzHost(cusolverSpH, &nnzL, &nnzU, info));
|
||||
|
||||
h_Plu = (int *)malloc(sizeof(int) * rowsA);
|
||||
h_Qlu = (int *)malloc(sizeof(int) * colsA);
|
||||
|
||||
h_csrValL = (double *)malloc(sizeof(double) * nnzL);
|
||||
h_csrRowPtrL = (int *)malloc(sizeof(int) * (rowsA + 1));
|
||||
h_csrColIndL = (int *)malloc(sizeof(int) * nnzL);
|
||||
|
||||
h_csrValU = (double *)malloc(sizeof(double) * nnzU);
|
||||
h_csrRowPtrU = (int *)malloc(sizeof(int) * (rowsA + 1));
|
||||
h_csrColIndU = (int *)malloc(sizeof(int) * nnzU);
|
||||
|
||||
assert(NULL != h_Plu);
|
||||
assert(NULL != h_Qlu);
|
||||
|
||||
assert(NULL != h_csrValL);
|
||||
assert(NULL != h_csrRowPtrL);
|
||||
assert(NULL != h_csrColIndL);
|
||||
|
||||
assert(NULL != h_csrValU);
|
||||
assert(NULL != h_csrRowPtrU);
|
||||
assert(NULL != h_csrColIndU);
|
||||
|
||||
checkCudaErrors(cusolverSpDcsrluExtractHost(cusolverSpH,
|
||||
h_Plu,
|
||||
h_Qlu,
|
||||
descrA,
|
||||
h_csrValL,
|
||||
h_csrRowPtrL,
|
||||
h_csrColIndL,
|
||||
descrA,
|
||||
h_csrValU,
|
||||
h_csrRowPtrU,
|
||||
h_csrColIndU,
|
||||
info,
|
||||
buffer_cpu));
|
||||
|
||||
stop = second();
|
||||
time_sp_extract = stop - start;
|
||||
|
||||
printf("nnzL = %d, nnzU = %d\n", nnzL, nnzU);
|
||||
|
||||
/* B = Qreorder*A*Qreorder^T
|
||||
* Plu*B*Qlu^T = L*U
|
||||
*
|
||||
* (Plu*Qreorder)*A*(Qlu*Qreorder)^T = L*U
|
||||
*
|
||||
* Let P = Plu*Qreroder, Q = Qlu*Qreorder,
|
||||
* then we have
|
||||
* P*A*Q^T = L*U
|
||||
* which is the fundamental relation in cusolverRf.
|
||||
*/
|
||||
printf("step 6: form P*A*Q^T = L*U\n");
|
||||
|
||||
h_P = (int *)malloc(sizeof(int) * rowsA);
|
||||
h_Q = (int *)malloc(sizeof(int) * colsA);
|
||||
assert(NULL != h_P);
|
||||
assert(NULL != h_Q);
|
||||
|
||||
printf("step 6.1: P = Plu*Qreroder\n");
|
||||
// gather operation, P = Qreorder(Plu)
|
||||
for (int j = 0; j < rowsA; j++) {
|
||||
h_P[j] = h_Qreorder[h_Plu[j]];
|
||||
}
|
||||
|
||||
printf("step 6.2: Q = Qlu*Qreorder \n");
|
||||
// gather operation, Q = Qreorder(Qlu)
|
||||
for (int j = 0; j < colsA; j++) {
|
||||
h_Q[j] = h_Qreorder[h_Qlu[j]];
|
||||
}
|
||||
|
||||
printf("step 7: create cusolverRf handle\n");
|
||||
checkCudaErrors(cusolverRfCreate(&cusolverRfH));
|
||||
|
||||
printf("step 8: set parameters for cusolverRf \n");
|
||||
// numerical values for checking "zeros" and for boosting.
|
||||
checkCudaErrors(cusolverRfSetNumericProperties(cusolverRfH, nzero, nboost));
|
||||
|
||||
// choose algorithm for refactorization and solve
|
||||
checkCudaErrors(cusolverRfSetAlgs(cusolverRfH, fact_alg, solve_alg));
|
||||
|
||||
// matrix mode: L and U are CSR format, and L has implicit unit diagonal
|
||||
checkCudaErrors(
|
||||
cusolverRfSetMatrixFormat(cusolverRfH, CUSOLVERRF_MATRIX_FORMAT_CSR, CUSOLVERRF_UNIT_DIAGONAL_ASSUMED_L));
|
||||
|
||||
// fast mode for matrix assembling
|
||||
checkCudaErrors(cusolverRfSetResetValuesFastMode(cusolverRfH, CUSOLVERRF_RESET_VALUES_FAST_MODE_ON));
|
||||
|
||||
printf("step 9: assemble P*A*Q = L*U \n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverRfSetupHost(rowsA,
|
||||
nnzA,
|
||||
h_csrRowPtrA,
|
||||
h_csrColIndA,
|
||||
h_csrValA,
|
||||
nnzL,
|
||||
h_csrRowPtrL,
|
||||
h_csrColIndL,
|
||||
h_csrValL,
|
||||
nnzU,
|
||||
h_csrRowPtrU,
|
||||
h_csrColIndU,
|
||||
h_csrValU,
|
||||
h_P,
|
||||
h_Q,
|
||||
cusolverRfH));
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
time_rf_assemble = stop - start;
|
||||
|
||||
printf("step 10: analyze to extract parallelism \n");
|
||||
checkCudaErrors(cusolverRfAnalyze(cusolverRfH));
|
||||
|
||||
printf("step 11: import A to cusolverRf \n");
|
||||
checkCudaErrors(cudaMemcpy(d_csrRowPtrA, h_csrRowPtrA, sizeof(int) * (rowsA + 1), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrColIndA, h_csrColIndA, sizeof(int) * nnzA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrValA, h_csrValA, sizeof(double) * nnzA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_P, h_P, sizeof(int) * rowsA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_Q, h_Q, sizeof(int) * colsA, cudaMemcpyHostToDevice));
|
||||
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverRfResetValues(rowsA, nnzA, d_csrRowPtrA, d_csrColIndA, d_csrValA, d_P, d_Q, cusolverRfH));
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
time_rf_reset = stop - start;
|
||||
|
||||
printf("step 12: refactorization \n");
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverRfRefactor(cusolverRfH));
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
time_rf_refactor = stop - start;
|
||||
|
||||
printf("step 13: solve A*x = b \n");
|
||||
checkCudaErrors(cudaMemcpy(d_x, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
|
||||
start = second();
|
||||
start = second();
|
||||
|
||||
checkCudaErrors(cusolverRfSolve(cusolverRfH, d_P, d_Q, 1, d_T, rowsA, d_x, rowsA));
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
stop = second();
|
||||
time_rf_solve = stop - start;
|
||||
|
||||
printf("step 14: evaluate residual r = b - A*x (result on GPU)\n");
|
||||
checkCudaErrors(cudaMemcpy(d_r, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseH,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(h_x, d_x, sizeof(double) * colsA, cudaMemcpyDeviceToHost));
|
||||
checkCudaErrors(cudaMemcpy(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost));
|
||||
|
||||
x_inf = vec_norminf(colsA, h_x);
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
printf("(GPU) |b - A*x| = %E \n", r_inf);
|
||||
printf("(GPU) |A| = %E \n", A_inf);
|
||||
printf("(GPU) |x| = %E \n", x_inf);
|
||||
printf("(GPU) |b - A*x|/(|A|*|x|) = %E \n", r_inf / (A_inf * x_inf));
|
||||
|
||||
printf("===== statistics \n");
|
||||
printf(" nnz(A) = %d, nnz(L+U) = %d, zero fill-in ratio = %f\n",
|
||||
nnzA,
|
||||
nnzL + nnzU,
|
||||
((double)(nnzL + nnzU)) / (double)nnzA);
|
||||
printf("\n");
|
||||
printf("===== timing profile \n");
|
||||
printf(" reorder A : %f sec\n", time_reorder);
|
||||
printf(" B = Q*A*Q^T : %f sec\n", time_perm);
|
||||
printf("\n");
|
||||
printf(" cusolverSp LU analysis: %f sec\n", time_sp_analysis);
|
||||
printf(" cusolverSp LU factor : %f sec\n", time_sp_factor);
|
||||
printf(" cusolverSp LU solve : %f sec\n", time_sp_solve);
|
||||
printf(" cusolverSp LU extract : %f sec\n", time_sp_extract);
|
||||
printf("\n");
|
||||
printf(" cusolverRf assemble : %f sec\n", time_rf_assemble);
|
||||
printf(" cusolverRf reset : %f sec\n", time_rf_reset);
|
||||
printf(" cusolverRf refactor : %f sec\n", time_rf_refactor);
|
||||
printf(" cusolverRf solve : %f sec\n", time_rf_solve);
|
||||
|
||||
if (cusolverRfH) {
|
||||
checkCudaErrors(cusolverRfDestroy(cusolverRfH));
|
||||
}
|
||||
if (cusolverSpH) {
|
||||
checkCudaErrors(cusolverSpDestroy(cusolverSpH));
|
||||
}
|
||||
if (cusparseH) {
|
||||
checkCudaErrors(cusparseDestroy(cusparseH));
|
||||
}
|
||||
if (stream) {
|
||||
checkCudaErrors(cudaStreamDestroy(stream));
|
||||
}
|
||||
if (descrA) {
|
||||
checkCudaErrors(cusparseDestroyMatDescr(descrA));
|
||||
}
|
||||
if (info) {
|
||||
checkCudaErrors(cusolverSpDestroyCsrluInfoHost(info));
|
||||
}
|
||||
|
||||
if (matA) {
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
}
|
||||
if (vecx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecx));
|
||||
}
|
||||
if (vecAx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecAx));
|
||||
}
|
||||
|
||||
if (h_csrValA) {
|
||||
free(h_csrValA);
|
||||
}
|
||||
if (h_csrRowPtrA) {
|
||||
free(h_csrRowPtrA);
|
||||
}
|
||||
if (h_csrColIndA) {
|
||||
free(h_csrColIndA);
|
||||
}
|
||||
|
||||
if (h_Qreorder) {
|
||||
free(h_Qreorder);
|
||||
}
|
||||
|
||||
if (h_csrRowPtrB) {
|
||||
free(h_csrRowPtrB);
|
||||
}
|
||||
if (h_csrColIndB) {
|
||||
free(h_csrColIndB);
|
||||
}
|
||||
if (h_csrValB) {
|
||||
free(h_csrValB);
|
||||
}
|
||||
if (h_mapBfromA) {
|
||||
free(h_mapBfromA);
|
||||
}
|
||||
|
||||
if (h_x) {
|
||||
free(h_x);
|
||||
}
|
||||
if (h_b) {
|
||||
free(h_b);
|
||||
}
|
||||
if (h_r) {
|
||||
free(h_r);
|
||||
}
|
||||
if (h_xhat) {
|
||||
free(h_xhat);
|
||||
}
|
||||
if (h_bhat) {
|
||||
free(h_bhat);
|
||||
}
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
|
||||
if (h_Plu) {
|
||||
free(h_Plu);
|
||||
}
|
||||
if (h_Qlu) {
|
||||
free(h_Qlu);
|
||||
}
|
||||
if (h_csrRowPtrL) {
|
||||
free(h_csrRowPtrL);
|
||||
}
|
||||
if (h_csrColIndL) {
|
||||
free(h_csrColIndL);
|
||||
}
|
||||
if (h_csrValL) {
|
||||
free(h_csrValL);
|
||||
}
|
||||
if (h_csrRowPtrU) {
|
||||
free(h_csrRowPtrU);
|
||||
}
|
||||
if (h_csrColIndU) {
|
||||
free(h_csrColIndU);
|
||||
}
|
||||
if (h_csrValU) {
|
||||
free(h_csrValU);
|
||||
}
|
||||
|
||||
if (h_P) {
|
||||
free(h_P);
|
||||
}
|
||||
if (h_Q) {
|
||||
free(h_Q);
|
||||
}
|
||||
|
||||
if (d_csrValA) {
|
||||
checkCudaErrors(cudaFree(d_csrValA));
|
||||
}
|
||||
if (d_csrRowPtrA) {
|
||||
checkCudaErrors(cudaFree(d_csrRowPtrA));
|
||||
}
|
||||
if (d_csrColIndA) {
|
||||
checkCudaErrors(cudaFree(d_csrColIndA));
|
||||
}
|
||||
if (d_x) {
|
||||
checkCudaErrors(cudaFree(d_x));
|
||||
}
|
||||
if (d_b) {
|
||||
checkCudaErrors(cudaFree(d_b));
|
||||
}
|
||||
if (d_r) {
|
||||
checkCudaErrors(cudaFree(d_r));
|
||||
}
|
||||
if (d_P) {
|
||||
checkCudaErrors(cudaFree(d_P));
|
||||
}
|
||||
if (d_Q) {
|
||||
checkCudaErrors(cudaFree(d_Q));
|
||||
}
|
||||
if (d_T) {
|
||||
checkCudaErrors(cudaFree(d_T));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
29803
cpp/4_CUDA_Libraries/cuSolverRf/lap2D_5pt_n100.mtx
Normal file
29803
cpp/4_CUDA_Libraries/cuSolverRf/lap2D_5pt_n100.mtx
Normal file
File diff suppressed because it is too large
Load Diff
30803
cpp/4_CUDA_Libraries/cuSolverRf/lap3D_7pt_n20.mtx
Normal file
30803
cpp/4_CUDA_Libraries/cuSolverRf/lap3D_7pt_n20.mtx
Normal file
File diff suppressed because it is too large
Load Diff
474
cpp/4_CUDA_Libraries/cuSolverRf/mmio.c
Normal file
474
cpp/4_CUDA_Libraries/cuSolverRf/mmio.c
Normal file
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_)
|
||||
{
|
||||
FILE *f;
|
||||
MM_typecode matcode;
|
||||
int M, N, nz;
|
||||
int i;
|
||||
double *val;
|
||||
int *I, *J;
|
||||
|
||||
if ((f = fopen(fname, "r")) == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
if (mm_read_banner(f, &matcode) != 0) {
|
||||
printf("mm_read_unsymetric: Could not process Matrix Market banner ");
|
||||
printf(" in file [%s]\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!(mm_is_real(matcode) && mm_is_matrix(matcode) && mm_is_sparse(matcode))) {
|
||||
fprintf(stderr, "Sorry, this application does not support ");
|
||||
fprintf(stderr, "Market Market type: [%s]\n", mm_typecode_to_str(matcode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find out size of sparse matrix: M, N, nz .... */
|
||||
|
||||
if (mm_read_mtx_crd_size(f, &M, &N, &nz) != 0) {
|
||||
fprintf(stderr, "read_unsymmetric_sparse(): could not parse matrix size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*M_ = M;
|
||||
*N_ = N;
|
||||
*nz_ = nz;
|
||||
|
||||
/* reserve memory for matrices */
|
||||
|
||||
I = (int *)malloc(nz * sizeof(int));
|
||||
J = (int *)malloc(nz * sizeof(int));
|
||||
val = (double *)malloc(nz * sizeof(double));
|
||||
|
||||
*val_ = val;
|
||||
*I_ = I;
|
||||
*J_ = J;
|
||||
|
||||
/* NOTE: when reading in doubles, ANSI C requires the use of the "l" */
|
||||
/* specifier as in "%lg", "%lf", "%le", otherwise errors will occur */
|
||||
/* (ANSI C X3.159-1989, Sec. 4.9.6.2, p. 136 lines 13-15) */
|
||||
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3) {
|
||||
return -1;
|
||||
}
|
||||
I[i]--; /* adjust from 1-based to 0-based */
|
||||
J[i]--;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_is_valid(MM_typecode matcode)
|
||||
{
|
||||
if (!mm_is_matrix(matcode))
|
||||
return 0;
|
||||
if (mm_is_dense(matcode) && mm_is_pattern(matcode))
|
||||
return 0;
|
||||
if (mm_is_real(matcode) && mm_is_hermitian(matcode))
|
||||
return 0;
|
||||
if (mm_is_pattern(matcode) && (mm_is_hermitian(matcode) || mm_is_skew(matcode)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
char banner[MM_MAX_TOKEN_LENGTH];
|
||||
char mtx[MM_MAX_TOKEN_LENGTH];
|
||||
char crd[MM_MAX_TOKEN_LENGTH];
|
||||
char data_type[MM_MAX_TOKEN_LENGTH];
|
||||
char storage_scheme[MM_MAX_TOKEN_LENGTH];
|
||||
char *p;
|
||||
|
||||
|
||||
mm_clear_typecode(matcode);
|
||||
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type, storage_scheme) != 5)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
for (p = mtx; *p != '\0'; *p = tolower(*p), p++)
|
||||
; /* convert to lower case */
|
||||
for (p = crd; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = data_type; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = storage_scheme; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
|
||||
/* check for banner */
|
||||
if (strncmp(banner, MatrixMarketBanner, strlen(MatrixMarketBanner)) != 0)
|
||||
return MM_NO_HEADER;
|
||||
|
||||
/* first field should be "mtx" */
|
||||
if (strcmp(mtx, MM_MTX_STR) != 0)
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
mm_set_matrix(matcode);
|
||||
|
||||
|
||||
/* second field describes whether this is a sparse matrix (in coordinate
|
||||
storgae) or a dense array */
|
||||
|
||||
|
||||
if (strcmp(crd, MM_SPARSE_STR) == 0)
|
||||
mm_set_sparse(matcode);
|
||||
else if (strcmp(crd, MM_DENSE_STR) == 0)
|
||||
mm_set_dense(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
/* third field */
|
||||
|
||||
if (strcmp(data_type, MM_REAL_STR) == 0)
|
||||
mm_set_real(matcode);
|
||||
else if (strcmp(data_type, MM_COMPLEX_STR) == 0)
|
||||
mm_set_complex(matcode);
|
||||
else if (strcmp(data_type, MM_PATTERN_STR) == 0)
|
||||
mm_set_pattern(matcode);
|
||||
else if (strcmp(data_type, MM_INT_STR) == 0)
|
||||
mm_set_integer(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
/* fourth field */
|
||||
|
||||
if (strcmp(storage_scheme, MM_GENERAL_STR) == 0)
|
||||
mm_set_general(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SYMM_STR) == 0)
|
||||
mm_set_symmetric(matcode);
|
||||
else if (strcmp(storage_scheme, MM_HERM_STR) == 0)
|
||||
mm_set_hermitian(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SKEW_STR) == 0)
|
||||
mm_set_skew(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz)
|
||||
{
|
||||
if (fprintf(f, "%d %d %d\n", M, N, nz) != 3)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = *nz = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d %d", M, N, nz) == 3)
|
||||
return 0;
|
||||
|
||||
else
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d %d", M, N, nz);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d", M, N) == 2)
|
||||
return 0;
|
||||
|
||||
else /* we have a blank line */
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d", M, N);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N)
|
||||
{
|
||||
if (fprintf(f, "%d %d\n", M, N) != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/******************************************************************/
|
||||
/* use when I[], J[], and val[]J, and val[] are already allocated */
|
||||
/******************************************************************/
|
||||
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
int i;
|
||||
if (mm_is_complex(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d %lg %lg", &I[i], &J[i], &val[2 * i], &val[2 * i + 1]) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d", &I[i], &J[i]) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *imag, MM_typecode matcode)
|
||||
{
|
||||
if (mm_is_complex(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg %lg", I, J, real, imag) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg\n", I, J, real) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
if (fscanf(f, "%d %d", I, J) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
mm_read_mtx_crd() fills M, N, nz, array of values, and return
|
||||
type code, e.g. 'MCRS'
|
||||
|
||||
if matrix is complex, values[] is of size 2*nz,
|
||||
(nz pairs of real/imaginary values)
|
||||
************************************************************************/
|
||||
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode)
|
||||
{
|
||||
int ret_code;
|
||||
FILE *f;
|
||||
|
||||
if (strcmp(fname, "stdin") == 0)
|
||||
f = stdin;
|
||||
else if ((f = fopen(fname, "r")) == NULL)
|
||||
return MM_COULD_NOT_READ_FILE;
|
||||
|
||||
|
||||
if ((ret_code = mm_read_banner(f, matcode)) != 0)
|
||||
return ret_code;
|
||||
|
||||
if (!(mm_is_valid(*matcode) && mm_is_sparse(*matcode) && mm_is_matrix(*matcode)))
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
if ((ret_code = mm_read_mtx_crd_size(f, M, N, nz)) != 0)
|
||||
return ret_code;
|
||||
|
||||
|
||||
*I = (int *)malloc(*nz * sizeof(int));
|
||||
*J = (int *)malloc(*nz * sizeof(int));
|
||||
*val = NULL;
|
||||
|
||||
if (mm_is_complex(*matcode)) {
|
||||
*val = (double *)malloc(*nz * 2 * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
else if (mm_is_real(*matcode) || mm_is_integer(*matcode)) {
|
||||
*val = (double *)malloc(*nz * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(*matcode)) {
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
if (f != stdin)
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode)
|
||||
{
|
||||
char *str = mm_typecode_to_str(matcode);
|
||||
int ret_code;
|
||||
|
||||
ret_code = fprintf(f, "%s %s\n", MatrixMarketBanner, str);
|
||||
free(str);
|
||||
if (ret_code != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
if (strcmp(fname, "stdout") == 0)
|
||||
f = stdout;
|
||||
else if ((f = fopen(fname, "w")) == NULL)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
|
||||
/* print banner followed by typecode */
|
||||
fprintf(f, "%s ", MatrixMarketBanner);
|
||||
fprintf(f, "%s\n", mm_typecode_to_str(matcode));
|
||||
|
||||
/* print matrix sizes and nonzeros */
|
||||
fprintf(f, "%d %d %d\n", M, N, nz);
|
||||
|
||||
/* print values */
|
||||
if (mm_is_pattern(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d\n", I[i], J[i]);
|
||||
else if (mm_is_integer(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %d\n", I[i], J[i], (int)val[i]);
|
||||
else if (mm_is_real(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g\n", I[i], J[i], val[i]);
|
||||
else if (mm_is_complex(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g %20.16g\n", I[i], J[i], val[2 * i], val[2 * i + 1]);
|
||||
else {
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new copy of a string s. mm_strdup() is a common routine, but
|
||||
* not part of ANSI C, so it is included here. Used by mm_typecode_to_str().
|
||||
*
|
||||
*/
|
||||
static char *mm_strdup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *s2 = (char *)malloc((len + 1) * sizeof(char));
|
||||
return strcpy(s2, s);
|
||||
}
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode)
|
||||
{
|
||||
char buffer[MM_MAX_LINE_LENGTH];
|
||||
char *types[4];
|
||||
// char *mm_strdup(const char *);
|
||||
// int error =0;
|
||||
|
||||
/* check for MTX type */
|
||||
if (mm_is_matrix(matcode))
|
||||
types[0] = MM_MTX_STR;
|
||||
else
|
||||
return NULL; // error=1;
|
||||
|
||||
/* check for CRD or ARR matrix */
|
||||
if (mm_is_sparse(matcode))
|
||||
types[1] = MM_SPARSE_STR;
|
||||
else if (mm_is_dense(matcode))
|
||||
types[1] = MM_DENSE_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* check for element data type */
|
||||
if (mm_is_real(matcode))
|
||||
types[2] = MM_REAL_STR;
|
||||
else if (mm_is_complex(matcode))
|
||||
types[2] = MM_COMPLEX_STR;
|
||||
else if (mm_is_pattern(matcode))
|
||||
types[2] = MM_PATTERN_STR;
|
||||
else if (mm_is_integer(matcode))
|
||||
types[2] = MM_INT_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
|
||||
/* check for symmetry type */
|
||||
if (mm_is_general(matcode))
|
||||
types[3] = MM_GENERAL_STR;
|
||||
else if (mm_is_symmetric(matcode))
|
||||
types[3] = MM_SYMM_STR;
|
||||
else if (mm_is_hermitian(matcode))
|
||||
types[3] = MM_HERM_STR;
|
||||
else if (mm_is_skew(matcode))
|
||||
types[3] = MM_SKEW_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
sprintf(buffer, "%s %s %s %s", types[0], types[1], types[2], types[3]);
|
||||
return mm_strdup(buffer);
|
||||
}
|
||||
138
cpp/4_CUDA_Libraries/cuSolverRf/mmio.h
Normal file
138
cpp/4_CUDA_Libraries/cuSolverRf/mmio.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MM_IO_H
|
||||
#define MM_IO_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define MM_MAX_LINE_LENGTH 1025
|
||||
#define MatrixMarketBanner "%%MatrixMarket"
|
||||
#define MM_MAX_TOKEN_LENGTH 64
|
||||
|
||||
typedef char MM_typecode[4];
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode);
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode);
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz);
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N);
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode);
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz);
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N);
|
||||
|
||||
|
||||
/********************* MM_typecode query fucntions ***************************/
|
||||
|
||||
#define mm_is_matrix(typecode) ((typecode)[0] == 'M')
|
||||
|
||||
#define mm_is_sparse(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_coordinate(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_dense(typecode) ((typecode)[1] == 'A')
|
||||
#define mm_is_array(typecode) ((typecode)[1] == 'A')
|
||||
|
||||
#define mm_is_complex(typecode) ((typecode)[2] == 'C')
|
||||
#define mm_is_real(typecode) ((typecode)[2] == 'R')
|
||||
#define mm_is_pattern(typecode) ((typecode)[2] == 'P')
|
||||
#define mm_is_integer(typecode) ((typecode)[2] == 'I')
|
||||
|
||||
#define mm_is_symmetric(typecode) ((typecode)[3] == 'S')
|
||||
#define mm_is_general(typecode) ((typecode)[3] == 'G')
|
||||
#define mm_is_skew(typecode) ((typecode)[3] == 'K')
|
||||
#define mm_is_hermitian(typecode) ((typecode)[3] == 'H')
|
||||
|
||||
int mm_is_valid(MM_typecode matcode); /* too complex for a macro */
|
||||
|
||||
|
||||
/********************* MM_typecode modify fucntions ***************************/
|
||||
|
||||
#define mm_set_matrix(typecode) ((*typecode)[0] = 'M')
|
||||
#define mm_set_coordinate(typecode) ((*typecode)[1] = 'C')
|
||||
#define mm_set_array(typecode) ((*typecode)[1] = 'A')
|
||||
#define mm_set_dense(typecode) mm_set_array(typecode)
|
||||
#define mm_set_sparse(typecode) mm_set_coordinate(typecode)
|
||||
|
||||
#define mm_set_complex(typecode) ((*typecode)[2] = 'C')
|
||||
#define mm_set_real(typecode) ((*typecode)[2] = 'R')
|
||||
#define mm_set_pattern(typecode) ((*typecode)[2] = 'P')
|
||||
#define mm_set_integer(typecode) ((*typecode)[2] = 'I')
|
||||
|
||||
|
||||
#define mm_set_symmetric(typecode) ((*typecode)[3] = 'S')
|
||||
#define mm_set_general(typecode) ((*typecode)[3] = 'G')
|
||||
#define mm_set_skew(typecode) ((*typecode)[3] = 'K')
|
||||
#define mm_set_hermitian(typecode) ((*typecode)[3] = 'H')
|
||||
|
||||
#define mm_clear_typecode(typecode) ((*typecode)[0] = (*typecode)[1] = (*typecode)[2] = ' ', (*typecode)[3] = 'G')
|
||||
|
||||
#define mm_initialize_typecode(typecode) mm_clear_typecode(typecode)
|
||||
|
||||
|
||||
/********************* Matrix Market error codes ***************************/
|
||||
|
||||
|
||||
#define MM_COULD_NOT_READ_FILE 11
|
||||
#define MM_PREMATURE_EOF 12
|
||||
#define MM_NOT_MTX 13
|
||||
#define MM_NO_HEADER 14
|
||||
#define MM_UNSUPPORTED_TYPE 15
|
||||
#define MM_LINE_TOO_LONG 16
|
||||
#define MM_COULD_NOT_WRITE_FILE 17
|
||||
|
||||
|
||||
/******************** Matrix Market internal definitions ********************
|
||||
|
||||
MM_matrix_typecode: 4-character sequence
|
||||
|
||||
ojbect sparse/ data storage
|
||||
dense type scheme
|
||||
|
||||
string position: [0] [1] [2] [3]
|
||||
|
||||
Matrix typecode: M(atrix) C(oord) R(eal) G(eneral)
|
||||
A(array) C(omplex) H(ermitian)
|
||||
P(attern) S(ymmetric)
|
||||
I(nteger) K(kew)
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#define MM_MTX_STR "matrix"
|
||||
#define MM_ARRAY_STR "array"
|
||||
#define MM_DENSE_STR "array"
|
||||
#define MM_COORDINATE_STR "coordinate"
|
||||
#define MM_SPARSE_STR "coordinate"
|
||||
#define MM_COMPLEX_STR "complex"
|
||||
#define MM_REAL_STR "real"
|
||||
#define MM_INT_STR "integer"
|
||||
#define MM_GENERAL_STR "general"
|
||||
#define MM_SYMM_STR "symmetric"
|
||||
#define MM_HERM_STR "hermitian"
|
||||
#define MM_SKEW_STR "skew-symmetric"
|
||||
#define MM_PATTERN_STR "pattern"
|
||||
|
||||
|
||||
/* high level routines */
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode);
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *img, MM_typecode matcode);
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
492
cpp/4_CUDA_Libraries/cuSolverRf/mmio_wrapper.cpp
Normal file
492
cpp/4_CUDA_Libraries/cuSolverRf/mmio_wrapper.cpp
Normal file
@@ -0,0 +1,492 @@
|
||||
/* 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 <cusolverDn.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
/* various __inline__ __device__ function to initialize a T_ELEM */
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(int);
|
||||
template <> __inline__ float cuGet<float>(int x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(int x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(int x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(int x) { return (make_cuDoubleComplex(double(x), 0.0)); }
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(int, int);
|
||||
template <> __inline__ float cuGet<float>(int x, int y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(int x, int y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(int x, int y) { return make_cuComplex(float(x), float(y)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(int x, int y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(float);
|
||||
template <> __inline__ float cuGet<float>(float x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(float x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(float x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(float x)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), 0.0));
|
||||
}
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(float, float);
|
||||
template <> __inline__ float cuGet<float>(float x, float y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(float x, float y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(float x, float y) { return (make_cuComplex(float(x), float(y))); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(float x, float y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(double);
|
||||
template <> __inline__ float cuGet<float>(double x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(double x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(double x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(double x)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), 0.0));
|
||||
}
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(double, double);
|
||||
template <> __inline__ float cuGet<float>(double x, double y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(double x, double y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(double x, double y) { return (make_cuComplex(float(x), float(y))); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(double x, double y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
static void compress_index(const int *Ind, int nnz, int m, int *Ptr, int base)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* initialize everything to zero */
|
||||
for (i = 0; i < m + 1; i++) {
|
||||
Ptr[i] = 0;
|
||||
}
|
||||
/* count elements in every row */
|
||||
Ptr[0] = base;
|
||||
for (i = 0; i < nnz; i++) {
|
||||
Ptr[Ind[i] + (1 - base)]++;
|
||||
}
|
||||
/* add all the values */
|
||||
for (i = 0; i < m; i++) {
|
||||
Ptr[i + 1] += Ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
struct cooFormat
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int p; // permutation
|
||||
};
|
||||
|
||||
int cmp_cooFormat_csr(struct cooFormat *s, struct cooFormat *t)
|
||||
{
|
||||
if (s->i < t->i) {
|
||||
return -1;
|
||||
}
|
||||
else if (s->i > t->i) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return s->j - t->j;
|
||||
}
|
||||
}
|
||||
|
||||
int cmp_cooFormat_csc(struct cooFormat *s, struct cooFormat *t)
|
||||
{
|
||||
if (s->j < t->j) {
|
||||
return -1;
|
||||
}
|
||||
else if (s->j > t->j) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return s->i - t->i;
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*FUNPTR)(const void *, const void *);
|
||||
typedef int (*FUNPTR2)(struct cooFormat *s, struct cooFormat *t);
|
||||
|
||||
static FUNPTR2 fptr_array[2] = {
|
||||
cmp_cooFormat_csr,
|
||||
cmp_cooFormat_csc,
|
||||
};
|
||||
|
||||
static int verify_pattern(int m, int nnz, int *csrRowPtr, int *csrColInd)
|
||||
{
|
||||
int i, col, start, end, base_index;
|
||||
int error_found = 0;
|
||||
|
||||
if (nnz != (csrRowPtr[m] - csrRowPtr[0])) {
|
||||
fprintf(stderr,
|
||||
"Error (nnz check failed): (csrRowPtr[%d]=%d - csrRowPtr[%d]=%d) "
|
||||
"!= (nnz=%d)\n",
|
||||
0,
|
||||
csrRowPtr[0],
|
||||
m,
|
||||
csrRowPtr[m],
|
||||
nnz);
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
base_index = csrRowPtr[0];
|
||||
if ((0 != base_index) && (1 != base_index)) {
|
||||
fprintf(stderr, "Error (base index check failed): base index = %d\n", base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
for (i = 0; (!error_found) && (i < m); i++) {
|
||||
start = csrRowPtr[i] - base_index;
|
||||
end = csrRowPtr[i + 1] - base_index;
|
||||
if (start > end) {
|
||||
fprintf(stderr,
|
||||
"Error (corrupted row): csrRowPtr[%d] (=%d) > csrRowPtr[%d] (=%d)\n",
|
||||
i,
|
||||
start + base_index,
|
||||
i + 1,
|
||||
end + base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
for (col = start; col < end; col++) {
|
||||
if (csrColInd[col] < base_index) {
|
||||
fprintf(stderr, "Error (column vs. base index check failed): csrColInd[%d] < %d\n", col, base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
if ((col < (end - 1)) && (csrColInd[col] >= csrColInd[col + 1])) {
|
||||
fprintf(stderr,
|
||||
"Error (sorting of the column indecis check failed): "
|
||||
"(csrColInd[%d]=%d) >= (csrColInd[%d]=%d)\n",
|
||||
col,
|
||||
csrColInd[col],
|
||||
col + 1,
|
||||
csrColInd[col + 1]);
|
||||
error_found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error_found;
|
||||
}
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix)
|
||||
{
|
||||
MM_typecode matcode;
|
||||
double *tempVal;
|
||||
int *tempRowInd, *tempColInd;
|
||||
double *tval;
|
||||
int *trow, *tcol;
|
||||
int *csrRowPtr, *cscColPtr;
|
||||
int i, j, error, base, count;
|
||||
struct cooFormat *work;
|
||||
|
||||
/* read the matrix */
|
||||
error = mm_read_mtx_crd(filename, m, n, nnz, &trow, &tcol, &tval, &matcode);
|
||||
if (error) {
|
||||
fprintf(stderr, "!!!! can not open file: '%s'\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start error checking */
|
||||
if (mm_is_complex(matcode) && ((elem_type != 'z') && (elem_type != 'c'))) {
|
||||
fprintf(stderr, "!!!! complex matrix requires type 'z' or 'c'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mm_is_dense(matcode) || mm_is_array(matcode) || mm_is_pattern(matcode) /*|| mm_is_integer(matcode)*/) {
|
||||
fprintf(stderr, "!!!! dense, array, pattern and integer matrices are not supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if necessary symmetrize the pattern (transform from triangular to full) */
|
||||
if ((extendSymMatrix) && (mm_is_symmetric(matcode) || mm_is_hermitian(matcode) || mm_is_skew(matcode))) {
|
||||
// count number of non-diagonal elements
|
||||
count = 0;
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
if (trow[i] != tcol[i]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// allocate space for the symmetrized matrix
|
||||
tempRowInd = (int *)malloc((*nnz + count) * sizeof(int));
|
||||
tempColInd = (int *)malloc((*nnz + count) * sizeof(int));
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
tempVal = (double *)malloc((*nnz + count) * sizeof(double));
|
||||
}
|
||||
else {
|
||||
tempVal = (double *)malloc(2 * (*nnz + count) * sizeof(double));
|
||||
}
|
||||
// copy the elements regular and transposed locations
|
||||
for (j = 0, i = 0; i < (*nnz); i++) {
|
||||
tempRowInd[j] = trow[i];
|
||||
tempColInd[j] = tcol[i];
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
tempVal[j] = tval[i];
|
||||
}
|
||||
else {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = tval[2 * i + 1];
|
||||
}
|
||||
j++;
|
||||
if (trow[i] != tcol[i]) {
|
||||
tempRowInd[j] = tcol[i];
|
||||
tempColInd[j] = trow[i];
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (mm_is_skew(matcode)) {
|
||||
tempVal[j] = -tval[i];
|
||||
}
|
||||
else {
|
||||
tempVal[j] = tval[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mm_is_hermitian(matcode)) {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = -tval[2 * i + 1];
|
||||
}
|
||||
else {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = tval[2 * i + 1];
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
(*nnz) += count;
|
||||
// free temporary storage
|
||||
free(trow);
|
||||
free(tcol);
|
||||
free(tval);
|
||||
}
|
||||
else {
|
||||
tempRowInd = trow;
|
||||
tempColInd = tcol;
|
||||
tempVal = tval;
|
||||
}
|
||||
// life time of (trow, tcol, tval) is over.
|
||||
// please use COO format (tempRowInd, tempColInd, tempVal)
|
||||
|
||||
// use qsort to sort COO format
|
||||
work = (struct cooFormat *)malloc(sizeof(struct cooFormat) * (*nnz));
|
||||
if (NULL == work) {
|
||||
fprintf(stderr, "!!!! allocation error, malloc failed\n");
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
work[i].i = tempRowInd[i];
|
||||
work[i].j = tempColInd[i];
|
||||
work[i].p = i; // permutation is identity
|
||||
}
|
||||
|
||||
if (csrFormat) {
|
||||
/* create row-major ordering of indices (sorted by row and within each row
|
||||
* by column) */
|
||||
qsort(work, *nnz, sizeof(struct cooFormat), (FUNPTR)fptr_array[0]);
|
||||
}
|
||||
else {
|
||||
/* create column-major ordering of indices (sorted by column and within each
|
||||
* column by row) */
|
||||
qsort(work, *nnz, sizeof(struct cooFormat), (FUNPTR)fptr_array[1]);
|
||||
}
|
||||
|
||||
// (tempRowInd, tempColInd) is sorted either by row-major or by col-major
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
tempRowInd[i] = work[i].i;
|
||||
tempColInd[i] = work[i].j;
|
||||
}
|
||||
|
||||
// setup base
|
||||
// check if there is any row/col 0, if so base-0
|
||||
// check if there is any row/col equal to matrix dimension m/n, if so base-1
|
||||
int base0 = 0;
|
||||
int base1 = 0;
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
const int row = tempRowInd[i];
|
||||
const int col = tempColInd[i];
|
||||
if ((0 == row) || (0 == col)) {
|
||||
base0 = 1;
|
||||
}
|
||||
if ((*m == row) || (*n == col)) {
|
||||
base1 = 1;
|
||||
}
|
||||
}
|
||||
if (base0 && base1) {
|
||||
printf("Error: input matrix is base-0 and base-1 \n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
base = 0;
|
||||
if (base1) {
|
||||
base = 1;
|
||||
}
|
||||
|
||||
/* compress the appropriate indices */
|
||||
if (csrFormat) {
|
||||
/* CSR format (assuming row-major format) */
|
||||
csrRowPtr = (int *)malloc(((*m) + 1) * sizeof(csrRowPtr[0]));
|
||||
if (!csrRowPtr)
|
||||
return 1;
|
||||
compress_index(tempRowInd, *nnz, *m, csrRowPtr, base);
|
||||
|
||||
*aRowInd = csrRowPtr;
|
||||
*aColInd = (int *)malloc((*nnz) * sizeof(int));
|
||||
}
|
||||
else {
|
||||
/* CSC format (assuming column-major format) */
|
||||
cscColPtr = (int *)malloc(((*n) + 1) * sizeof(cscColPtr[0]));
|
||||
if (!cscColPtr)
|
||||
return 1;
|
||||
compress_index(tempColInd, *nnz, *n, cscColPtr, base);
|
||||
|
||||
*aColInd = cscColPtr;
|
||||
*aRowInd = (int *)malloc((*nnz) * sizeof(int));
|
||||
}
|
||||
|
||||
/* transfrom the matrix values of type double into one of the cusparse library
|
||||
* types */
|
||||
*aVal = (T_ELEM *)malloc((*nnz) * sizeof(T_ELEM));
|
||||
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
if (csrFormat) {
|
||||
(*aColInd)[i] = tempColInd[i];
|
||||
}
|
||||
else {
|
||||
(*aRowInd)[i] = tempRowInd[i];
|
||||
}
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
(*aVal)[i] = cuGet<T_ELEM>(tempVal[work[i].p]);
|
||||
}
|
||||
else {
|
||||
(*aVal)[i] = cuGet<T_ELEM>(tempVal[2 * work[i].p], tempVal[2 * work[i].p + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for corruption */
|
||||
int error_found;
|
||||
if (csrFormat) {
|
||||
error_found = verify_pattern(*m, *nnz, *aRowInd, *aColInd);
|
||||
}
|
||||
else {
|
||||
error_found = verify_pattern(*n, *nnz, *aColInd, *aRowInd);
|
||||
}
|
||||
if (error_found) {
|
||||
fprintf(stderr, "!!!! verify_pattern failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cleanup and exit */
|
||||
free(work);
|
||||
free(tempVal);
|
||||
free(tempColInd);
|
||||
free(tempRowInd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* specific instantiation */
|
||||
template int loadMMSparseMatrix<float>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
float **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<double>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
double **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<cuComplex>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
cuComplex **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<cuDoubleComplex>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
cuDoubleComplex **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
18
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
58
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/CMakeLists.txt
Normal file
58
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/CMakeLists.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cuSolverSp_LinearSolver LANGUAGES C CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cuSolverSp_LinearSolver
|
||||
add_executable(cuSolverSp_LinearSolver cuSolverSp_LinearSolver.cpp mmio.c mmio_wrapper.cpp)
|
||||
|
||||
target_compile_options(cuSolverSp_LinearSolver PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cuSolverSp_LinearSolver PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cuSolverSp_LinearSolver PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(cuSolverSp_LinearSolver PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(cuSolverSp_LinearSolver PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusolver
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverSp_LinearSolver POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap2D_5pt_n100.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverSp_LinearSolver POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap3D_7pt_n20.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
39
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/README.md
Normal file
39
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# cuSolverSp_LinearSolver - cuSolverSp Linear Solver
|
||||
|
||||
## Description
|
||||
|
||||
A CUDA Sample that demonstrates cuSolverSP's LU, QR and Cholesky factorization.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUSOLVER Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuDoubleComplex, cuComplex
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaStreamDestroy, cudaFree, cudaDeviceSynchronize, cudaMalloc, cudaStreamCreate, cudaMemcpyAsync
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUSOLVER](../../../README.md#cusolver), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,738 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Test three linear solvers, including Cholesky, LU and QR.
|
||||
* The user has to prepare a sparse matrix of "matrix market format" (with
|
||||
extension .mtx).
|
||||
* For example, the user can download matrices in Florida Sparse Matrix
|
||||
Collection.
|
||||
* (http://www.cise.ufl.edu/research/sparse/matrices/)
|
||||
*
|
||||
* The user needs to choose a solver by the switch -R<solver> and
|
||||
* to provide the path of the matrix by the switch -F<file>, then
|
||||
* the program solves
|
||||
* A*x = b
|
||||
* and reports relative error
|
||||
* |b-A*x|/(|A|*|x|+|b|)
|
||||
*
|
||||
* How does it work?
|
||||
* The example solves A*x = b by the following steps
|
||||
* step 1: B = A(Q,Q)
|
||||
* Q is the ordering to minimize zero fill-in.
|
||||
* The user can choose symrcm or symamd.
|
||||
* step 2: solve B*z = Q*b
|
||||
* step 3: x = inv(Q)*z
|
||||
*
|
||||
* Above three steps can be combined by the formula
|
||||
* (Q*A*Q')*(Q*x) = (Q*b)
|
||||
*
|
||||
* The elapsed time is also reported so the user can compare efficiency of
|
||||
different solvers.
|
||||
*
|
||||
* How to use
|
||||
/cuSolverSp_LinearSolver // Default: Cholesky, symrcm &
|
||||
file=lap2D_5pt_n100.mtx
|
||||
* ./cuSolverSp_LinearSolver -R=chol -file=<file> // cholesky
|
||||
factorization
|
||||
* ./cuSolverSp_LinearSolver -R=lu -P=symrcm -file=<file> // symrcm + LU
|
||||
with partial pivoting
|
||||
* ./cuSolverSp_LinearSolver -R=qr -P=symamd -file=<file> // symamd + QR
|
||||
factorization
|
||||
*
|
||||
*
|
||||
* Remark: the absolute error on solution x is meaningless without knowing
|
||||
condition number of A.
|
||||
* The relative error on residual should be close to machine zero,
|
||||
i.e. 1.e-15.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cusolverSp.h"
|
||||
#include "cusparse.h"
|
||||
#include "helper_cuda.h"
|
||||
#include "helper_cusolver.h"
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
void UsageSP(void)
|
||||
{
|
||||
printf("<options>\n");
|
||||
printf("-h : display this help\n");
|
||||
printf("-R=<name> : choose a linear solver\n");
|
||||
printf(" chol (cholesky factorization), this is default\n");
|
||||
printf(" qr (QR factorization)\n");
|
||||
printf(" lu (LU factorization)\n");
|
||||
printf("-P=<name> : choose a reordering\n");
|
||||
printf(" symrcm (Reverse Cuthill-McKee)\n");
|
||||
printf(" symamd (Approximate Minimum Degree)\n");
|
||||
printf(" metis (nested dissection)\n");
|
||||
printf("-file=<filename> : filename containing a matrix in MM format\n");
|
||||
printf("-device=<device_id> : <device_id> if want to run on specific GPU\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void parseCommandLineArguments(int argc, char *argv[], struct testOpts &opts)
|
||||
{
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "-h")) {
|
||||
UsageSP();
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "R")) {
|
||||
char *solverType = NULL;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "R", &solverType);
|
||||
|
||||
if (solverType) {
|
||||
if ((STRCASECMP(solverType, "chol") != 0) && (STRCASECMP(solverType, "lu") != 0)
|
||||
&& (STRCASECMP(solverType, "qr") != 0)) {
|
||||
printf("\nIncorrect argument passed to -R option\n");
|
||||
UsageSP();
|
||||
}
|
||||
else {
|
||||
opts.testFunc = solverType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "P")) {
|
||||
char *reorderType = NULL;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "P", &reorderType);
|
||||
|
||||
if (reorderType) {
|
||||
if ((STRCASECMP(reorderType, "symrcm") != 0) && (STRCASECMP(reorderType, "symamd") != 0)
|
||||
&& (STRCASECMP(reorderType, "metis") != 0)) {
|
||||
printf("\nIncorrect argument passed to -P option\n");
|
||||
UsageSP();
|
||||
}
|
||||
else {
|
||||
opts.reorder = reorderType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
|
||||
char *fileName = 0;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "file", &fileName);
|
||||
|
||||
if (fileName) {
|
||||
opts.sparse_mat_filename = fileName;
|
||||
}
|
||||
else {
|
||||
printf("\nIncorrect filename passed to -file \n ");
|
||||
UsageSP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct testOpts opts;
|
||||
cusolverSpHandle_t handle = NULL;
|
||||
cusparseHandle_t cusparseHandle = NULL; /* used in residual evaluation */
|
||||
cudaStream_t stream = NULL;
|
||||
cusparseMatDescr_t descrA = NULL;
|
||||
|
||||
int rowsA = 0; /* number of rows of A */
|
||||
int colsA = 0; /* number of columns of A */
|
||||
int nnzA = 0; /* number of nonzeros of A */
|
||||
int baseA = 0; /* base index in CSR format */
|
||||
|
||||
/* CSR(A) from I/O */
|
||||
int *h_csrRowPtrA = NULL;
|
||||
int *h_csrColIndA = NULL;
|
||||
double *h_csrValA = NULL;
|
||||
|
||||
double *h_z = NULL; /* z = B \ (Q*b) */
|
||||
double *h_x = NULL; /* x = A \ b */
|
||||
double *h_b = NULL; /* b = ones(n,1) */
|
||||
double *h_Qb = NULL; /* Q*b */
|
||||
double *h_r = NULL; /* r = b - A*x */
|
||||
|
||||
int *h_Q = NULL; /* <int> n */
|
||||
/* reorder to reduce zero fill-in */
|
||||
/* Q = symrcm(A) or Q = symamd(A) */
|
||||
/* B = Q*A*Q' or B = A(Q,Q) by MATLAB notation */
|
||||
int *h_csrRowPtrB = NULL; /* <int> n+1 */
|
||||
int *h_csrColIndB = NULL; /* <int> nnzA */
|
||||
double *h_csrValB = NULL; /* <double> nnzA */
|
||||
int *h_mapBfromA = NULL; /* <int> nnzA */
|
||||
|
||||
size_t size_perm = 0;
|
||||
void *buffer_cpu = NULL; /* working space for permutation: B = Q*A*Q^T */
|
||||
|
||||
/* device copy of A: used in residual evaluation */
|
||||
int *d_csrRowPtrA = NULL;
|
||||
int *d_csrColIndA = NULL;
|
||||
double *d_csrValA = NULL;
|
||||
|
||||
/* device copy of B: used in B*z = Q*b */
|
||||
int *d_csrRowPtrB = NULL;
|
||||
int *d_csrColIndB = NULL;
|
||||
double *d_csrValB = NULL;
|
||||
|
||||
int *d_Q = NULL; /* device copy of h_Q */
|
||||
double *d_z = NULL; /* z = B \ Q*b */
|
||||
double *d_x = NULL; /* x = A \ b */
|
||||
double *d_b = NULL; /* a copy of h_b */
|
||||
double *d_Qb = NULL; /* a copy of h_Qb */
|
||||
double *d_r = NULL; /* r = b - A*x */
|
||||
|
||||
double tol = 1.e-12;
|
||||
const int reorder = 0; /* no reordering */
|
||||
int singularity = 0; /* -1 if A is invertible under tol. */
|
||||
|
||||
/* the constants are used in residual evaluation, r = b - A*x */
|
||||
const double minus_one = -1.0;
|
||||
const double one = 1.0;
|
||||
|
||||
double b_inf = 0.0;
|
||||
double x_inf = 0.0;
|
||||
double r_inf = 0.0;
|
||||
double A_inf = 0.0;
|
||||
int errors = 0;
|
||||
int issym = 0;
|
||||
|
||||
double start, stop;
|
||||
double time_solve_cpu;
|
||||
double time_solve_gpu;
|
||||
|
||||
parseCommandLineArguments(argc, argv, opts);
|
||||
|
||||
if (NULL == opts.testFunc) {
|
||||
opts.testFunc = "chol"; /* By default running Cholesky as NO solver selected with -R
|
||||
option. */
|
||||
}
|
||||
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (opts.sparse_mat_filename == NULL) {
|
||||
opts.sparse_mat_filename = sdkFindFilePath("lap2D_5pt_n100.mtx", argv[0]);
|
||||
if (opts.sparse_mat_filename != NULL)
|
||||
printf("Using default input file [%s]\n", opts.sparse_mat_filename);
|
||||
else
|
||||
printf("Could not find lap2D_5pt_n100.mtx\n");
|
||||
}
|
||||
else {
|
||||
printf("Using input file [%s]\n", opts.sparse_mat_filename);
|
||||
}
|
||||
|
||||
printf("step 1: read matrix market format\n");
|
||||
|
||||
if (opts.sparse_mat_filename == NULL) {
|
||||
fprintf(stderr, "Error: input matrix is not provided\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (loadMMSparseMatrix<double>(opts.sparse_mat_filename,
|
||||
'd',
|
||||
true,
|
||||
&rowsA,
|
||||
&colsA,
|
||||
&nnzA,
|
||||
&h_csrValA,
|
||||
&h_csrRowPtrA,
|
||||
&h_csrColIndA,
|
||||
true)) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
baseA = h_csrRowPtrA[0]; // baseA = {0,1}
|
||||
printf("sparse matrix A is %d x %d with %d nonzeros, base=%d\n", rowsA, colsA, nnzA, baseA);
|
||||
|
||||
if (rowsA != colsA) {
|
||||
fprintf(stderr, "Error: only support square matrix\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
checkCudaErrors(cusolverSpCreate(&handle));
|
||||
checkCudaErrors(cusparseCreate(&cusparseHandle));
|
||||
|
||||
checkCudaErrors(cudaStreamCreate(&stream));
|
||||
/* bind stream to cusparse and cusolver*/
|
||||
checkCudaErrors(cusolverSpSetStream(handle, stream));
|
||||
checkCudaErrors(cusparseSetStream(cusparseHandle, stream));
|
||||
|
||||
/* configure matrix descriptor*/
|
||||
checkCudaErrors(cusparseCreateMatDescr(&descrA));
|
||||
checkCudaErrors(cusparseSetMatType(descrA, CUSPARSE_MATRIX_TYPE_GENERAL));
|
||||
if (baseA) {
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descrA, CUSPARSE_INDEX_BASE_ONE));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descrA, CUSPARSE_INDEX_BASE_ZERO));
|
||||
}
|
||||
|
||||
h_z = (double *)malloc(sizeof(double) * colsA);
|
||||
h_x = (double *)malloc(sizeof(double) * colsA);
|
||||
h_b = (double *)malloc(sizeof(double) * rowsA);
|
||||
h_Qb = (double *)malloc(sizeof(double) * rowsA);
|
||||
h_r = (double *)malloc(sizeof(double) * rowsA);
|
||||
|
||||
h_Q = (int *)malloc(sizeof(int) * colsA);
|
||||
h_csrRowPtrB = (int *)malloc(sizeof(int) * (rowsA + 1));
|
||||
h_csrColIndB = (int *)malloc(sizeof(int) * nnzA);
|
||||
h_csrValB = (double *)malloc(sizeof(double) * nnzA);
|
||||
h_mapBfromA = (int *)malloc(sizeof(int) * nnzA);
|
||||
|
||||
assert(NULL != h_z);
|
||||
assert(NULL != h_x);
|
||||
assert(NULL != h_b);
|
||||
assert(NULL != h_Qb);
|
||||
assert(NULL != h_r);
|
||||
assert(NULL != h_Q);
|
||||
assert(NULL != h_csrRowPtrB);
|
||||
assert(NULL != h_csrColIndB);
|
||||
assert(NULL != h_csrValB);
|
||||
assert(NULL != h_mapBfromA);
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrRowPtrA, sizeof(int) * (rowsA + 1)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrColIndA, sizeof(int) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrValA, sizeof(double) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrRowPtrB, sizeof(int) * (rowsA + 1)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrColIndB, sizeof(int) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrValB, sizeof(double) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_Q, sizeof(int) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_z, sizeof(double) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, sizeof(double) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_b, sizeof(double) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_Qb, sizeof(double) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, sizeof(double) * rowsA));
|
||||
|
||||
/* verify if A has symmetric pattern or not */
|
||||
checkCudaErrors(
|
||||
cusolverSpXcsrissymHost(handle, rowsA, nnzA, descrA, h_csrRowPtrA, h_csrRowPtrA + 1, h_csrColIndA, &issym));
|
||||
|
||||
if (0 == strcmp(opts.testFunc, "chol")) {
|
||||
if (!issym) {
|
||||
printf("Error: A has no symmetric pattern, please use LU or QR \n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
printf("step 2: reorder the matrix A to minimize zero fill-in\n");
|
||||
printf(" if the user choose a reordering by -P=symrcm, -P=symamd or "
|
||||
"-P=metis\n");
|
||||
|
||||
if (NULL != opts.reorder) {
|
||||
if (0 == strcmp(opts.reorder, "symrcm")) {
|
||||
printf("step 2.1: Q = symrcm(A) \n");
|
||||
checkCudaErrors(cusolverSpXcsrsymrcmHost(handle, rowsA, nnzA, descrA, h_csrRowPtrA, h_csrColIndA, h_Q));
|
||||
}
|
||||
else if (0 == strcmp(opts.reorder, "symamd")) {
|
||||
printf("step 2.1: Q = symamd(A) \n");
|
||||
checkCudaErrors(cusolverSpXcsrsymamdHost(handle, rowsA, nnzA, descrA, h_csrRowPtrA, h_csrColIndA, h_Q));
|
||||
}
|
||||
else if (0 == strcmp(opts.reorder, "metis")) {
|
||||
printf("step 2.1: Q = metis(A) \n");
|
||||
checkCudaErrors(cusolverSpXcsrmetisndHost(handle,
|
||||
rowsA,
|
||||
nnzA,
|
||||
descrA,
|
||||
h_csrRowPtrA,
|
||||
h_csrColIndA,
|
||||
NULL, /* default setting. */
|
||||
h_Q));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: %s is unknown reordering\n", opts.reorder);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("step 2.1: no reordering is chosen, Q = 0:n-1 \n");
|
||||
for (int j = 0; j < rowsA; j++) {
|
||||
h_Q[j] = j;
|
||||
}
|
||||
}
|
||||
|
||||
printf("step 2.2: B = A(Q,Q) \n");
|
||||
|
||||
memcpy(h_csrRowPtrB, h_csrRowPtrA, sizeof(int) * (rowsA + 1));
|
||||
memcpy(h_csrColIndB, h_csrColIndA, sizeof(int) * nnzA);
|
||||
|
||||
checkCudaErrors(cusolverSpXcsrperm_bufferSizeHost(
|
||||
handle, rowsA, colsA, nnzA, descrA, h_csrRowPtrB, h_csrColIndB, h_Q, h_Q, &size_perm));
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
buffer_cpu = (void *)malloc(sizeof(char) * size_perm);
|
||||
assert(NULL != buffer_cpu);
|
||||
|
||||
/* h_mapBfromA = Identity */
|
||||
for (int j = 0; j < nnzA; j++) {
|
||||
h_mapBfromA[j] = j;
|
||||
}
|
||||
checkCudaErrors(cusolverSpXcsrpermHost(
|
||||
handle, rowsA, colsA, nnzA, descrA, h_csrRowPtrB, h_csrColIndB, h_Q, h_Q, h_mapBfromA, buffer_cpu));
|
||||
|
||||
/* B = A( mapBfromA ) */
|
||||
for (int j = 0; j < nnzA; j++) {
|
||||
h_csrValB[j] = h_csrValA[h_mapBfromA[j]];
|
||||
}
|
||||
|
||||
printf("step 3: b(j) = 1 + j/n \n");
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
h_b[row] = 1.0 + ((double)row) / ((double)rowsA);
|
||||
}
|
||||
|
||||
/* h_Qb = b(Q) */
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
h_Qb[row] = h_b[h_Q[row]];
|
||||
}
|
||||
|
||||
printf("step 4: prepare data on device\n");
|
||||
checkCudaErrors(
|
||||
cudaMemcpyAsync(d_csrRowPtrA, h_csrRowPtrA, sizeof(int) * (rowsA + 1), cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_csrColIndA, h_csrColIndA, sizeof(int) * nnzA, cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_csrValA, h_csrValA, sizeof(double) * nnzA, cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(
|
||||
cudaMemcpyAsync(d_csrRowPtrB, h_csrRowPtrB, sizeof(int) * (rowsA + 1), cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_csrColIndB, h_csrColIndB, sizeof(int) * nnzA, cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_csrValB, h_csrValB, sizeof(double) * nnzA, cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_b, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_Qb, h_Qb, sizeof(double) * rowsA, cudaMemcpyHostToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_Q, h_Q, sizeof(int) * rowsA, cudaMemcpyHostToDevice, stream));
|
||||
|
||||
printf("step 5: solve A*x = b on CPU \n");
|
||||
start = second();
|
||||
|
||||
/* solve B*z = Q*b */
|
||||
if (0 == strcmp(opts.testFunc, "chol")) {
|
||||
checkCudaErrors(cusolverSpDcsrlsvcholHost(
|
||||
handle, rowsA, nnzA, descrA, h_csrValB, h_csrRowPtrB, h_csrColIndB, h_Qb, tol, reorder, h_z, &singularity));
|
||||
}
|
||||
else if (0 == strcmp(opts.testFunc, "lu")) {
|
||||
checkCudaErrors(cusolverSpDcsrlsvluHost(
|
||||
handle, rowsA, nnzA, descrA, h_csrValB, h_csrRowPtrB, h_csrColIndB, h_Qb, tol, reorder, h_z, &singularity));
|
||||
}
|
||||
else if (0 == strcmp(opts.testFunc, "qr")) {
|
||||
checkCudaErrors(cusolverSpDcsrlsvqrHost(
|
||||
handle, rowsA, nnzA, descrA, h_csrValB, h_csrRowPtrB, h_csrColIndB, h_Qb, tol, reorder, h_z, &singularity));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: %s is unknown function\n", opts.testFunc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Q*x = z */
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
h_x[h_Q[row]] = h_z[row];
|
||||
}
|
||||
|
||||
if (0 <= singularity) {
|
||||
printf("WARNING: the matrix is singular at row %d under tol (%E)\n", singularity, tol);
|
||||
}
|
||||
|
||||
stop = second();
|
||||
time_solve_cpu = stop - start;
|
||||
|
||||
printf("step 6: evaluate residual r = b - A*x (result on CPU)\n");
|
||||
checkCudaErrors(cudaMemcpyAsync(d_r, d_b, sizeof(double) * rowsA, cudaMemcpyDeviceToDevice, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(d_x, h_x, sizeof(double) * colsA, cudaMemcpyHostToDevice, stream));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
if (baseA) {
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
d_csrRowPtrA,
|
||||
d_csrColIndA,
|
||||
d_csrValA,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ONE,
|
||||
CUDA_R_64F));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
d_csrRowPtrA,
|
||||
d_csrColIndA,
|
||||
d_csrValA,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_64F));
|
||||
}
|
||||
|
||||
cusparseDnVecDescr_t vecx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecx, colsA, d_x, CUDA_R_64F));
|
||||
cusparseDnVecDescr_t vecAx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecAx, rowsA, d_r, CUDA_R_64F));
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
size_t bufferSize = 0;
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSize));
|
||||
void *buffer = NULL;
|
||||
checkCudaErrors(cudaMalloc(&buffer, bufferSize));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost, stream));
|
||||
/* wait until h_r is ready */
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
b_inf = vec_norminf(rowsA, h_b);
|
||||
x_inf = vec_norminf(colsA, h_x);
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
A_inf = csr_mat_norminf(rowsA, colsA, nnzA, descrA, h_csrValA, h_csrRowPtrA, h_csrColIndA);
|
||||
|
||||
printf("(CPU) |b - A*x| = %E \n", r_inf);
|
||||
printf("(CPU) |A| = %E \n", A_inf);
|
||||
printf("(CPU) |x| = %E \n", x_inf);
|
||||
printf("(CPU) |b| = %E \n", b_inf);
|
||||
printf("(CPU) |b - A*x|/(|A|*|x| + |b|) = %E \n", r_inf / (A_inf * x_inf + b_inf));
|
||||
|
||||
printf("step 7: solve A*x = b on GPU\n");
|
||||
start = second();
|
||||
|
||||
/* solve B*z = Q*b */
|
||||
if (0 == strcmp(opts.testFunc, "chol")) {
|
||||
checkCudaErrors(cusolverSpDcsrlsvchol(
|
||||
handle, rowsA, nnzA, descrA, d_csrValB, d_csrRowPtrB, d_csrColIndB, d_Qb, tol, reorder, d_z, &singularity));
|
||||
}
|
||||
else if (0 == strcmp(opts.testFunc, "lu")) {
|
||||
printf("WARNING: no LU available on GPU \n");
|
||||
}
|
||||
else if (0 == strcmp(opts.testFunc, "qr")) {
|
||||
checkCudaErrors(cusolverSpDcsrlsvqr(
|
||||
handle, rowsA, nnzA, descrA, d_csrValB, d_csrRowPtrB, d_csrColIndB, d_Qb, tol, reorder, d_z, &singularity));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: %s is unknow function\n", opts.testFunc);
|
||||
return 1;
|
||||
}
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
if (0 <= singularity) {
|
||||
printf("WARNING: the matrix is singular at row %d under tol (%E)\n", singularity, tol);
|
||||
}
|
||||
/* Q*x = z */
|
||||
cusparseSpVecDescr_t vecz = NULL;
|
||||
checkCudaErrors(
|
||||
cusparseCreateSpVec(&vecz, colsA, rowsA, d_Q, d_z, CUSPARSE_INDEX_32I, CUSPARSE_INDEX_BASE_ZERO, CUDA_R_64F));
|
||||
checkCudaErrors(cusparseScatter(cusparseHandle, vecz, vecx));
|
||||
checkCudaErrors(cusparseDestroySpVec(vecz));
|
||||
|
||||
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
stop = second();
|
||||
time_solve_gpu = stop - start;
|
||||
|
||||
printf("step 8: evaluate residual r = b - A*x (result on GPU)\n");
|
||||
checkCudaErrors(cudaMemcpyAsync(d_r, d_b, sizeof(double) * rowsA, cudaMemcpyDeviceToDevice, stream));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseHandle,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemcpyAsync(h_x, d_x, sizeof(double) * colsA, cudaMemcpyDeviceToHost, stream));
|
||||
checkCudaErrors(cudaMemcpyAsync(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost, stream));
|
||||
/* wait until h_x and h_r are ready */
|
||||
checkCudaErrors(cudaDeviceSynchronize());
|
||||
|
||||
b_inf = vec_norminf(rowsA, h_b);
|
||||
x_inf = vec_norminf(colsA, h_x);
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
|
||||
if (0 != strcmp(opts.testFunc, "lu")) {
|
||||
// only cholesky and qr have GPU version
|
||||
printf("(GPU) |b - A*x| = %E \n", r_inf);
|
||||
printf("(GPU) |A| = %E \n", A_inf);
|
||||
printf("(GPU) |x| = %E \n", x_inf);
|
||||
printf("(GPU) |b| = %E \n", b_inf);
|
||||
printf("(GPU) |b - A*x|/(|A|*|x| + |b|) = %E \n", r_inf / (A_inf * x_inf + b_inf));
|
||||
}
|
||||
|
||||
fprintf(stdout, "timing %s: CPU = %10.6f sec , GPU = %10.6f sec\n", opts.testFunc, time_solve_cpu, time_solve_gpu);
|
||||
|
||||
if (0 != strcmp(opts.testFunc, "lu")) {
|
||||
printf("show last 10 elements of solution vector (GPU) \n");
|
||||
printf("consistent result for different reordering and solver \n");
|
||||
for (int j = rowsA - 10; j < rowsA; j++) {
|
||||
printf("x[%d] = %E\n", j, h_x[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (handle) {
|
||||
checkCudaErrors(cusolverSpDestroy(handle));
|
||||
}
|
||||
if (cusparseHandle) {
|
||||
checkCudaErrors(cusparseDestroy(cusparseHandle));
|
||||
}
|
||||
if (stream) {
|
||||
checkCudaErrors(cudaStreamDestroy(stream));
|
||||
}
|
||||
if (descrA) {
|
||||
checkCudaErrors(cusparseDestroyMatDescr(descrA));
|
||||
}
|
||||
if (matA) {
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
}
|
||||
if (vecx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecx));
|
||||
}
|
||||
if (vecAx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecAx));
|
||||
}
|
||||
|
||||
if (h_csrValA) {
|
||||
free(h_csrValA);
|
||||
}
|
||||
if (h_csrRowPtrA) {
|
||||
free(h_csrRowPtrA);
|
||||
}
|
||||
if (h_csrColIndA) {
|
||||
free(h_csrColIndA);
|
||||
}
|
||||
if (h_z) {
|
||||
free(h_z);
|
||||
}
|
||||
if (h_x) {
|
||||
free(h_x);
|
||||
}
|
||||
if (h_b) {
|
||||
free(h_b);
|
||||
}
|
||||
if (h_Qb) {
|
||||
free(h_Qb);
|
||||
}
|
||||
if (h_r) {
|
||||
free(h_r);
|
||||
}
|
||||
|
||||
if (h_Q) {
|
||||
free(h_Q);
|
||||
}
|
||||
|
||||
if (h_csrRowPtrB) {
|
||||
free(h_csrRowPtrB);
|
||||
}
|
||||
if (h_csrColIndB) {
|
||||
free(h_csrColIndB);
|
||||
}
|
||||
if (h_csrValB) {
|
||||
free(h_csrValB);
|
||||
}
|
||||
if (h_mapBfromA) {
|
||||
free(h_mapBfromA);
|
||||
}
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
checkCudaErrors(cudaFree(buffer));
|
||||
}
|
||||
if (d_csrValA) {
|
||||
checkCudaErrors(cudaFree(d_csrValA));
|
||||
}
|
||||
if (d_csrRowPtrA) {
|
||||
checkCudaErrors(cudaFree(d_csrRowPtrA));
|
||||
}
|
||||
if (d_csrColIndA) {
|
||||
checkCudaErrors(cudaFree(d_csrColIndA));
|
||||
}
|
||||
if (d_csrValB) {
|
||||
checkCudaErrors(cudaFree(d_csrValB));
|
||||
}
|
||||
if (d_csrRowPtrB) {
|
||||
checkCudaErrors(cudaFree(d_csrRowPtrB));
|
||||
}
|
||||
if (d_csrColIndB) {
|
||||
checkCudaErrors(cudaFree(d_csrColIndB));
|
||||
}
|
||||
if (d_Q) {
|
||||
checkCudaErrors(cudaFree(d_Q));
|
||||
}
|
||||
if (d_z) {
|
||||
checkCudaErrors(cudaFree(d_z));
|
||||
}
|
||||
if (d_x) {
|
||||
checkCudaErrors(cudaFree(d_x));
|
||||
}
|
||||
if (d_b) {
|
||||
checkCudaErrors(cudaFree(d_b));
|
||||
}
|
||||
if (d_Qb) {
|
||||
checkCudaErrors(cudaFree(d_Qb));
|
||||
}
|
||||
if (d_r) {
|
||||
checkCudaErrors(cudaFree(d_r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
29803
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/lap2D_5pt_n100.mtx
Normal file
29803
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/lap2D_5pt_n100.mtx
Normal file
File diff suppressed because it is too large
Load Diff
30803
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/lap3D_7pt_n20.mtx
Normal file
30803
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/lap3D_7pt_n20.mtx
Normal file
File diff suppressed because it is too large
Load Diff
477
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/mmio.c
Normal file
477
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/mmio.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_)
|
||||
{
|
||||
FILE *f;
|
||||
MM_typecode matcode;
|
||||
int M, N, nz;
|
||||
int i;
|
||||
double *val;
|
||||
int *I, *J;
|
||||
|
||||
if ((f = fopen(fname, "r")) == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
if (mm_read_banner(f, &matcode) != 0) {
|
||||
printf("mm_read_unsymetric: Could not process Matrix Market banner ");
|
||||
printf(" in file [%s]\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!(mm_is_real(matcode) && mm_is_matrix(matcode) && mm_is_sparse(matcode))) {
|
||||
fprintf(stderr, "Sorry, this application does not support ");
|
||||
fprintf(stderr, "Market Market type: [%s]\n", mm_typecode_to_str(matcode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find out size of sparse matrix: M, N, nz .... */
|
||||
|
||||
if (mm_read_mtx_crd_size(f, &M, &N, &nz) != 0) {
|
||||
fprintf(stderr, "read_unsymmetric_sparse(): could not parse matrix size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*M_ = M;
|
||||
*N_ = N;
|
||||
*nz_ = nz;
|
||||
|
||||
/* reserve memory for matrices */
|
||||
|
||||
I = (int *)malloc(nz * sizeof(int));
|
||||
J = (int *)malloc(nz * sizeof(int));
|
||||
val = (double *)malloc(nz * sizeof(double));
|
||||
|
||||
*val_ = val;
|
||||
*I_ = I;
|
||||
*J_ = J;
|
||||
|
||||
/* NOTE: when reading in doubles, ANSI C requires the use of the "l" */
|
||||
/* specifier as in "%lg", "%lf", "%le", otherwise errors will occur */
|
||||
/* (ANSI C X3.159-1989, Sec. 4.9.6.2, p. 136 lines 13-15) */
|
||||
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3) {
|
||||
return -1;
|
||||
}
|
||||
I[i]--; /* adjust from 1-based to 0-based */
|
||||
J[i]--;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_is_valid(MM_typecode matcode)
|
||||
{
|
||||
if (!mm_is_matrix(matcode))
|
||||
return 0;
|
||||
if (mm_is_dense(matcode) && mm_is_pattern(matcode))
|
||||
return 0;
|
||||
if (mm_is_real(matcode) && mm_is_hermitian(matcode))
|
||||
return 0;
|
||||
if (mm_is_pattern(matcode) && (mm_is_hermitian(matcode) || mm_is_skew(matcode)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
char banner[MM_MAX_TOKEN_LENGTH];
|
||||
char mtx[MM_MAX_TOKEN_LENGTH];
|
||||
char crd[MM_MAX_TOKEN_LENGTH];
|
||||
char data_type[MM_MAX_TOKEN_LENGTH];
|
||||
char storage_scheme[MM_MAX_TOKEN_LENGTH];
|
||||
char *p;
|
||||
|
||||
|
||||
mm_clear_typecode(matcode);
|
||||
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type, storage_scheme) != 5)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
for (p = mtx; *p != '\0'; *p = tolower(*p), p++)
|
||||
; /* convert to lower case */
|
||||
for (p = crd; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = data_type; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = storage_scheme; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
|
||||
/* check for banner */
|
||||
if (strncmp(banner, MatrixMarketBanner, strlen(MatrixMarketBanner)) != 0)
|
||||
return MM_NO_HEADER;
|
||||
|
||||
/* first field should be "mtx" */
|
||||
if (strcmp(mtx, MM_MTX_STR) != 0)
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
mm_set_matrix(matcode);
|
||||
|
||||
|
||||
/* second field describes whether this is a sparse matrix (in coordinate
|
||||
storage) or a dense array */
|
||||
|
||||
|
||||
if (strcmp(crd, MM_SPARSE_STR) == 0)
|
||||
mm_set_sparse(matcode);
|
||||
else if (strcmp(crd, MM_DENSE_STR) == 0)
|
||||
mm_set_dense(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
/* third field */
|
||||
|
||||
if (strcmp(data_type, MM_REAL_STR) == 0)
|
||||
mm_set_real(matcode);
|
||||
else if (strcmp(data_type, MM_COMPLEX_STR) == 0)
|
||||
mm_set_complex(matcode);
|
||||
else if (strcmp(data_type, MM_PATTERN_STR) == 0)
|
||||
mm_set_pattern(matcode);
|
||||
else if (strcmp(data_type, MM_INT_STR) == 0)
|
||||
mm_set_integer(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
/* fourth field */
|
||||
|
||||
if (strcmp(storage_scheme, MM_GENERAL_STR) == 0)
|
||||
mm_set_general(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SYMM_STR) == 0)
|
||||
mm_set_symmetric(matcode);
|
||||
else if (strcmp(storage_scheme, MM_HERM_STR) == 0)
|
||||
mm_set_hermitian(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SKEW_STR) == 0)
|
||||
mm_set_skew(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz)
|
||||
{
|
||||
if (fprintf(f, "%d %d %d\n", M, N, nz) != 3)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = *nz = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d %d", M, N, nz) == 3)
|
||||
return 0;
|
||||
|
||||
else
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d %d", M, N, nz);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d", M, N) == 2)
|
||||
return 0;
|
||||
|
||||
else /* we have a blank line */
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d", M, N);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N)
|
||||
{
|
||||
if (fprintf(f, "%d %d\n", M, N) != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/******************************************************************/
|
||||
/* use when I[], J[], and val[]J, and val[] are already allocated */
|
||||
/******************************************************************/
|
||||
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
int i;
|
||||
if (mm_is_complex(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d %lg %lg", &I[i], &J[i], &val[2 * i], &val[2 * i + 1]) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d", &I[i], &J[i]) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *imag, MM_typecode matcode)
|
||||
{
|
||||
if (mm_is_complex(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg %lg", I, J, real, imag) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg\n", I, J, real) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
if (fscanf(f, "%d %d", I, J) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
mm_read_mtx_crd() fills M, N, nz, array of values, and return
|
||||
type code, e.g. 'MCRS'
|
||||
|
||||
if matrix is complex, values[] is of size 2*nz,
|
||||
(nz pairs of real/imaginary values)
|
||||
************************************************************************/
|
||||
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode)
|
||||
{
|
||||
int ret_code;
|
||||
FILE *f;
|
||||
|
||||
if (strcmp(fname, "stdin") == 0)
|
||||
f = stdin;
|
||||
else if ((f = fopen(fname, "r")) == NULL)
|
||||
return MM_COULD_NOT_READ_FILE;
|
||||
|
||||
|
||||
if ((ret_code = mm_read_banner(f, matcode)) != 0)
|
||||
return ret_code;
|
||||
|
||||
if (!(mm_is_valid(*matcode) && mm_is_sparse(*matcode) && mm_is_matrix(*matcode)))
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
if ((ret_code = mm_read_mtx_crd_size(f, M, N, nz)) != 0)
|
||||
return ret_code;
|
||||
|
||||
|
||||
*I = (int *)malloc(*nz * sizeof(int));
|
||||
*J = (int *)malloc(*nz * sizeof(int));
|
||||
*val = NULL;
|
||||
|
||||
if (mm_is_complex(*matcode)) {
|
||||
*val = (double *)malloc(*nz * 2 * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
else if (mm_is_real(*matcode) || mm_is_integer(*matcode)) {
|
||||
*val = (double *)malloc(*nz * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(*matcode)) {
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
if (f != stdin)
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode)
|
||||
{
|
||||
char *str = mm_typecode_to_str(matcode);
|
||||
int ret_code;
|
||||
|
||||
ret_code = fprintf(f, "%s %s\n", MatrixMarketBanner, str);
|
||||
free(str);
|
||||
if (ret_code != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
if (strcmp(fname, "stdout") == 0)
|
||||
f = stdout;
|
||||
else if ((f = fopen(fname, "w")) == NULL)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
|
||||
/* print banner followed by typecode */
|
||||
fprintf(f, "%s ", MatrixMarketBanner);
|
||||
fprintf(f, "%s\n", mm_typecode_to_str(matcode));
|
||||
|
||||
/* print matrix sizes and nonzeros */
|
||||
fprintf(f, "%d %d %d\n", M, N, nz);
|
||||
|
||||
/* print values */
|
||||
if (mm_is_pattern(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d\n", I[i], J[i]);
|
||||
else if (mm_is_integer(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %d\n", I[i], J[i], (int)val[i]);
|
||||
else if (mm_is_real(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g\n", I[i], J[i], val[i]);
|
||||
else if (mm_is_complex(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g %20.16g\n", I[i], J[i], val[2 * i], val[2 * i + 1]);
|
||||
else {
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new copy of a string s. mm_strdup() is a common routine, but
|
||||
* not part of ANSI C, so it is included here. Used by mm_typecode_to_str().
|
||||
*
|
||||
*/
|
||||
static char *mm_strdup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *s2 = (char *)malloc((len + 1) * sizeof(char));
|
||||
return strcpy(s2, s);
|
||||
}
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode)
|
||||
{
|
||||
char buffer[MM_MAX_LINE_LENGTH];
|
||||
char *types[4];
|
||||
// char *mm_strdup(const char *);
|
||||
// int error =0;
|
||||
|
||||
/* check for MTX type */
|
||||
if (mm_is_matrix(matcode))
|
||||
types[0] = MM_MTX_STR;
|
||||
else
|
||||
return NULL; // error=1;
|
||||
|
||||
/* check for CRD or ARR matrix */
|
||||
if (mm_is_sparse(matcode))
|
||||
types[1] = MM_SPARSE_STR;
|
||||
else if (mm_is_dense(matcode))
|
||||
types[1] = MM_DENSE_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* check for element data type */
|
||||
if (mm_is_real(matcode))
|
||||
types[2] = MM_REAL_STR;
|
||||
else if (mm_is_complex(matcode))
|
||||
types[2] = MM_COMPLEX_STR;
|
||||
else if (mm_is_pattern(matcode))
|
||||
types[2] = MM_PATTERN_STR;
|
||||
else if (mm_is_integer(matcode))
|
||||
types[2] = MM_INT_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
|
||||
/* check for symmetry type */
|
||||
if (mm_is_general(matcode))
|
||||
types[3] = MM_GENERAL_STR;
|
||||
else if (mm_is_symmetric(matcode))
|
||||
types[3] = MM_SYMM_STR;
|
||||
else if (mm_is_hermitian(matcode))
|
||||
types[3] = MM_HERM_STR;
|
||||
else if (mm_is_skew(matcode))
|
||||
types[3] = MM_SKEW_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
sprintf(buffer, "%s %s %s %s", types[0], types[1], types[2], types[3]);
|
||||
return mm_strdup(buffer);
|
||||
}
|
||||
138
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/mmio.h
Normal file
138
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/mmio.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MM_IO_H
|
||||
#define MM_IO_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define MM_MAX_LINE_LENGTH 1025
|
||||
#define MatrixMarketBanner "%%MatrixMarket"
|
||||
#define MM_MAX_TOKEN_LENGTH 64
|
||||
|
||||
typedef char MM_typecode[4];
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode);
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode);
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz);
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N);
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode);
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz);
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N);
|
||||
|
||||
|
||||
/********************* MM_typecode query fucntions ***************************/
|
||||
|
||||
#define mm_is_matrix(typecode) ((typecode)[0] == 'M')
|
||||
|
||||
#define mm_is_sparse(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_coordinate(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_dense(typecode) ((typecode)[1] == 'A')
|
||||
#define mm_is_array(typecode) ((typecode)[1] == 'A')
|
||||
|
||||
#define mm_is_complex(typecode) ((typecode)[2] == 'C')
|
||||
#define mm_is_real(typecode) ((typecode)[2] == 'R')
|
||||
#define mm_is_pattern(typecode) ((typecode)[2] == 'P')
|
||||
#define mm_is_integer(typecode) ((typecode)[2] == 'I')
|
||||
|
||||
#define mm_is_symmetric(typecode) ((typecode)[3] == 'S')
|
||||
#define mm_is_general(typecode) ((typecode)[3] == 'G')
|
||||
#define mm_is_skew(typecode) ((typecode)[3] == 'K')
|
||||
#define mm_is_hermitian(typecode) ((typecode)[3] == 'H')
|
||||
|
||||
int mm_is_valid(MM_typecode matcode); /* too complex for a macro */
|
||||
|
||||
|
||||
/********************* MM_typecode modify fucntions ***************************/
|
||||
|
||||
#define mm_set_matrix(typecode) ((*typecode)[0] = 'M')
|
||||
#define mm_set_coordinate(typecode) ((*typecode)[1] = 'C')
|
||||
#define mm_set_array(typecode) ((*typecode)[1] = 'A')
|
||||
#define mm_set_dense(typecode) mm_set_array(typecode)
|
||||
#define mm_set_sparse(typecode) mm_set_coordinate(typecode)
|
||||
|
||||
#define mm_set_complex(typecode) ((*typecode)[2] = 'C')
|
||||
#define mm_set_real(typecode) ((*typecode)[2] = 'R')
|
||||
#define mm_set_pattern(typecode) ((*typecode)[2] = 'P')
|
||||
#define mm_set_integer(typecode) ((*typecode)[2] = 'I')
|
||||
|
||||
|
||||
#define mm_set_symmetric(typecode) ((*typecode)[3] = 'S')
|
||||
#define mm_set_general(typecode) ((*typecode)[3] = 'G')
|
||||
#define mm_set_skew(typecode) ((*typecode)[3] = 'K')
|
||||
#define mm_set_hermitian(typecode) ((*typecode)[3] = 'H')
|
||||
|
||||
#define mm_clear_typecode(typecode) ((*typecode)[0] = (*typecode)[1] = (*typecode)[2] = ' ', (*typecode)[3] = 'G')
|
||||
|
||||
#define mm_initialize_typecode(typecode) mm_clear_typecode(typecode)
|
||||
|
||||
|
||||
/********************* Matrix Market error codes ***************************/
|
||||
|
||||
|
||||
#define MM_COULD_NOT_READ_FILE 11
|
||||
#define MM_PREMATURE_EOF 12
|
||||
#define MM_NOT_MTX 13
|
||||
#define MM_NO_HEADER 14
|
||||
#define MM_UNSUPPORTED_TYPE 15
|
||||
#define MM_LINE_TOO_LONG 16
|
||||
#define MM_COULD_NOT_WRITE_FILE 17
|
||||
|
||||
|
||||
/******************** Matrix Market internal definitions ********************
|
||||
|
||||
MM_matrix_typecode: 4-character sequence
|
||||
|
||||
ojbect sparse/ data storage
|
||||
dense type scheme
|
||||
|
||||
string position: [0] [1] [2] [3]
|
||||
|
||||
Matrix typecode: M(atrix) C(oord) R(eal) G(eneral)
|
||||
A(array) C(omplex) H(ermitian)
|
||||
P(attern) S(ymmetric)
|
||||
I(nteger) K(kew)
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#define MM_MTX_STR "matrix"
|
||||
#define MM_ARRAY_STR "array"
|
||||
#define MM_DENSE_STR "array"
|
||||
#define MM_COORDINATE_STR "coordinate"
|
||||
#define MM_SPARSE_STR "coordinate"
|
||||
#define MM_COMPLEX_STR "complex"
|
||||
#define MM_REAL_STR "real"
|
||||
#define MM_INT_STR "integer"
|
||||
#define MM_GENERAL_STR "general"
|
||||
#define MM_SYMM_STR "symmetric"
|
||||
#define MM_HERM_STR "hermitian"
|
||||
#define MM_SKEW_STR "skew-symmetric"
|
||||
#define MM_PATTERN_STR "pattern"
|
||||
|
||||
|
||||
/* high level routines */
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode);
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *img, MM_typecode matcode);
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
473
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/mmio_wrapper.cpp
Normal file
473
cpp/4_CUDA_Libraries/cuSolverSp_LinearSolver/mmio_wrapper.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
|
||||
#include <cusolverDn.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
/* various __inline__ __device__ function to initialize a T_ELEM */
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(int);
|
||||
template <> __inline__ float cuGet<float>(int x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(int x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(int x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(int x) { return (make_cuDoubleComplex(double(x), 0.0)); }
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(int, int);
|
||||
template <> __inline__ float cuGet<float>(int x, int y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(int x, int y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(int x, int y) { return make_cuComplex(float(x), float(y)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(int x, int y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(float);
|
||||
template <> __inline__ float cuGet<float>(float x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(float x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(float x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(float x)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), 0.0));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(float, float);
|
||||
template <> __inline__ float cuGet<float>(float x, float y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(float x, float y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(float x, float y) { return (make_cuComplex(float(x), float(y))); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(float x, float y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(double);
|
||||
template <> __inline__ float cuGet<float>(double x) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(double x) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(double x) { return (make_cuComplex(float(x), 0.0f)); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(double x)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), 0.0));
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM> __inline__ T_ELEM cuGet(double, double);
|
||||
template <> __inline__ float cuGet<float>(double x, double y) { return float(x); }
|
||||
|
||||
template <> __inline__ double cuGet<double>(double x, double y) { return double(x); }
|
||||
|
||||
template <> __inline__ cuComplex cuGet<cuComplex>(double x, double y) { return (make_cuComplex(float(x), float(y))); }
|
||||
|
||||
template <> __inline__ cuDoubleComplex cuGet<cuDoubleComplex>(double x, double y)
|
||||
{
|
||||
return (make_cuDoubleComplex(double(x), double(y)));
|
||||
}
|
||||
|
||||
|
||||
static void compress_index(const int *Ind, int nnz, int m, int *Ptr, int base)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* initialize everything to zero */
|
||||
for (i = 0; i < m + 1; i++) {
|
||||
Ptr[i] = 0;
|
||||
}
|
||||
/* count elements in every row */
|
||||
Ptr[0] = base;
|
||||
for (i = 0; i < nnz; i++) {
|
||||
Ptr[Ind[i] + (1 - base)]++;
|
||||
}
|
||||
/* add all the values */
|
||||
for (i = 0; i < m; i++) {
|
||||
Ptr[i + 1] += Ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct cooFormat
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int p; // permutation
|
||||
};
|
||||
|
||||
|
||||
int cmp_cooFormat_csr(struct cooFormat *s, struct cooFormat *t)
|
||||
{
|
||||
if (s->i < t->i) {
|
||||
return -1;
|
||||
}
|
||||
else if (s->i > t->i) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return s->j - t->j;
|
||||
}
|
||||
}
|
||||
|
||||
int cmp_cooFormat_csc(struct cooFormat *s, struct cooFormat *t)
|
||||
{
|
||||
if (s->j < t->j) {
|
||||
return -1;
|
||||
}
|
||||
else if (s->j > t->j) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return s->i - t->i;
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*FUNPTR)(const void *, const void *);
|
||||
typedef int (*FUNPTR2)(struct cooFormat *s, struct cooFormat *t);
|
||||
|
||||
static FUNPTR2 fptr_array[2] = {
|
||||
cmp_cooFormat_csr,
|
||||
cmp_cooFormat_csc,
|
||||
};
|
||||
|
||||
|
||||
static int verify_pattern(int m, int nnz, int *csrRowPtr, int *csrColInd)
|
||||
{
|
||||
int i, col, start, end, base_index;
|
||||
int error_found = 0;
|
||||
|
||||
if (nnz != (csrRowPtr[m] - csrRowPtr[0])) {
|
||||
fprintf(stderr,
|
||||
"Error (nnz check failed): (csrRowPtr[%d]=%d - csrRowPtr[%d]=%d) != (nnz=%d)\n",
|
||||
0,
|
||||
csrRowPtr[0],
|
||||
m,
|
||||
csrRowPtr[m],
|
||||
nnz);
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
base_index = csrRowPtr[0];
|
||||
if ((0 != base_index) && (1 != base_index)) {
|
||||
fprintf(stderr, "Error (base index check failed): base index = %d\n", base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
|
||||
for (i = 0; (!error_found) && (i < m); i++) {
|
||||
start = csrRowPtr[i] - base_index;
|
||||
end = csrRowPtr[i + 1] - base_index;
|
||||
if (start > end) {
|
||||
fprintf(stderr,
|
||||
"Error (corrupted row): csrRowPtr[%d] (=%d) > csrRowPtr[%d] (=%d)\n",
|
||||
i,
|
||||
start + base_index,
|
||||
i + 1,
|
||||
end + base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
for (col = start; col < end; col++) {
|
||||
if (csrColInd[col] < base_index) {
|
||||
fprintf(stderr, "Error (column vs. base index check failed): csrColInd[%d] < %d\n", col, base_index);
|
||||
error_found = 1;
|
||||
}
|
||||
if ((col < (end - 1)) && (csrColInd[col] >= csrColInd[col + 1])) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error (sorting of the column indecis check failed): (csrColInd[%d]=%d) >= (csrColInd[%d]=%d)\n",
|
||||
col,
|
||||
csrColInd[col],
|
||||
col + 1,
|
||||
csrColInd[col + 1]);
|
||||
error_found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error_found;
|
||||
}
|
||||
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix)
|
||||
{
|
||||
MM_typecode matcode;
|
||||
double *tempVal;
|
||||
int *tempRowInd, *tempColInd;
|
||||
double *tval;
|
||||
int *trow, *tcol;
|
||||
int *csrRowPtr, *cscColPtr;
|
||||
int i, j, error, base, count;
|
||||
struct cooFormat *work;
|
||||
|
||||
/* read the matrix */
|
||||
error = mm_read_mtx_crd(filename, m, n, nnz, &trow, &tcol, &tval, &matcode);
|
||||
if (error) {
|
||||
fprintf(stderr, "!!!! can not open file: '%s'\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start error checking */
|
||||
if (mm_is_complex(matcode) && ((elem_type != 'z') && (elem_type != 'c'))) {
|
||||
fprintf(stderr, "!!!! complex matrix requires type 'z' or 'c'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mm_is_dense(matcode) || mm_is_array(matcode) || mm_is_pattern(matcode) /*|| mm_is_integer(matcode)*/) {
|
||||
fprintf(stderr, "!!!! dense, array, pattern and integer matrices are not supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if necessary symmetrize the pattern (transform from triangular to full) */
|
||||
if ((extendSymMatrix) && (mm_is_symmetric(matcode) || mm_is_hermitian(matcode) || mm_is_skew(matcode))) {
|
||||
// count number of non-diagonal elements
|
||||
count = 0;
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
if (trow[i] != tcol[i]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// allocate space for the symmetrized matrix
|
||||
tempRowInd = (int *)malloc((*nnz + count) * sizeof(int));
|
||||
tempColInd = (int *)malloc((*nnz + count) * sizeof(int));
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
tempVal = (double *)malloc((*nnz + count) * sizeof(double));
|
||||
}
|
||||
else {
|
||||
tempVal = (double *)malloc(2 * (*nnz + count) * sizeof(double));
|
||||
}
|
||||
// copy the elements regular and transposed locations
|
||||
for (j = 0, i = 0; i < (*nnz); i++) {
|
||||
tempRowInd[j] = trow[i];
|
||||
tempColInd[j] = tcol[i];
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
tempVal[j] = tval[i];
|
||||
}
|
||||
else {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = tval[2 * i + 1];
|
||||
}
|
||||
j++;
|
||||
if (trow[i] != tcol[i]) {
|
||||
tempRowInd[j] = tcol[i];
|
||||
tempColInd[j] = trow[i];
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (mm_is_skew(matcode)) {
|
||||
tempVal[j] = -tval[i];
|
||||
}
|
||||
else {
|
||||
tempVal[j] = tval[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mm_is_hermitian(matcode)) {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = -tval[2 * i + 1];
|
||||
}
|
||||
else {
|
||||
tempVal[2 * j] = tval[2 * i];
|
||||
tempVal[2 * j + 1] = tval[2 * i + 1];
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
(*nnz) += count;
|
||||
// free temporary storage
|
||||
free(trow);
|
||||
free(tcol);
|
||||
free(tval);
|
||||
}
|
||||
else {
|
||||
tempRowInd = trow;
|
||||
tempColInd = tcol;
|
||||
tempVal = tval;
|
||||
}
|
||||
// life time of (trow, tcol, tval) is over.
|
||||
// please use COO format (tempRowInd, tempColInd, tempVal)
|
||||
|
||||
// use qsort to sort COO format
|
||||
work = (struct cooFormat *)malloc(sizeof(struct cooFormat) * (*nnz));
|
||||
if (NULL == work) {
|
||||
fprintf(stderr, "!!!! allocation error, malloc failed\n");
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
work[i].i = tempRowInd[i];
|
||||
work[i].j = tempColInd[i];
|
||||
work[i].p = i; // permutation is identity
|
||||
}
|
||||
|
||||
if (csrFormat) {
|
||||
/* create row-major ordering of indices (sorted by row and within each row by column) */
|
||||
qsort(work, *nnz, sizeof(struct cooFormat), (FUNPTR)fptr_array[0]);
|
||||
}
|
||||
else {
|
||||
/* create column-major ordering of indices (sorted by column and within each column by row) */
|
||||
qsort(work, *nnz, sizeof(struct cooFormat), (FUNPTR)fptr_array[1]);
|
||||
}
|
||||
|
||||
// (tempRowInd, tempColInd) is sorted either by row-major or by col-major
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
tempRowInd[i] = work[i].i;
|
||||
tempColInd[i] = work[i].j;
|
||||
}
|
||||
|
||||
// setup base
|
||||
// check if there is any row/col 0, if so base-0
|
||||
// check if there is any row/col equal to matrix dimension m/n, if so base-1
|
||||
int base0 = 0;
|
||||
int base1 = 0;
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
const int row = tempRowInd[i];
|
||||
const int col = tempColInd[i];
|
||||
if ((0 == row) || (0 == col)) {
|
||||
base0 = 1;
|
||||
}
|
||||
if ((*m == row) || (*n == col)) {
|
||||
base1 = 1;
|
||||
}
|
||||
}
|
||||
if (base0 && base1) {
|
||||
printf("Error: input matrix is base-0 and base-1 \n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
base = 0;
|
||||
if (base1) {
|
||||
base = 1;
|
||||
}
|
||||
|
||||
/* compress the appropriate indices */
|
||||
if (csrFormat) {
|
||||
/* CSR format (assuming row-major format) */
|
||||
csrRowPtr = (int *)malloc(((*m) + 1) * sizeof(csrRowPtr[0]));
|
||||
if (!csrRowPtr)
|
||||
return 1;
|
||||
compress_index(tempRowInd, *nnz, *m, csrRowPtr, base);
|
||||
|
||||
*aRowInd = csrRowPtr;
|
||||
*aColInd = (int *)malloc((*nnz) * sizeof(int));
|
||||
}
|
||||
else {
|
||||
/* CSC format (assuming column-major format) */
|
||||
cscColPtr = (int *)malloc(((*n) + 1) * sizeof(cscColPtr[0]));
|
||||
if (!cscColPtr)
|
||||
return 1;
|
||||
compress_index(tempColInd, *nnz, *n, cscColPtr, base);
|
||||
|
||||
*aColInd = cscColPtr;
|
||||
*aRowInd = (int *)malloc((*nnz) * sizeof(int));
|
||||
}
|
||||
|
||||
/* transfrom the matrix values of type double into one of the cusparse library types */
|
||||
*aVal = (T_ELEM *)malloc((*nnz) * sizeof(T_ELEM));
|
||||
|
||||
for (i = 0; i < (*nnz); i++) {
|
||||
if (csrFormat) {
|
||||
(*aColInd)[i] = tempColInd[i];
|
||||
}
|
||||
else {
|
||||
(*aRowInd)[i] = tempRowInd[i];
|
||||
}
|
||||
if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
(*aVal)[i] = cuGet<T_ELEM>(tempVal[work[i].p]);
|
||||
}
|
||||
else {
|
||||
(*aVal)[i] = cuGet<T_ELEM>(tempVal[2 * work[i].p], tempVal[2 * work[i].p + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for corruption */
|
||||
int error_found;
|
||||
if (csrFormat) {
|
||||
error_found = verify_pattern(*m, *nnz, *aRowInd, *aColInd);
|
||||
}
|
||||
else {
|
||||
error_found = verify_pattern(*n, *nnz, *aColInd, *aRowInd);
|
||||
}
|
||||
if (error_found) {
|
||||
fprintf(stderr, "!!!! verify_pattern failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cleanup and exit */
|
||||
free(work);
|
||||
free(tempVal);
|
||||
free(tempColInd);
|
||||
free(tempRowInd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* specific instantiation */
|
||||
template int loadMMSparseMatrix<float>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
float **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<double>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
double **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<cuComplex>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
cuComplex **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
template int loadMMSparseMatrix<cuDoubleComplex>(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
cuDoubleComplex **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
18
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/.vscode/c_cpp_properties.json
vendored
Normal file
18
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../../Common"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/local/cuda/bin/nvcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
7
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/.vscode/extensions.json
vendored
Normal file
7
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nvidia.nsight-vscode-edition",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.makefile-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
|
||||
|
||||
project(cuSolverSp_LowlevelCholesky LANGUAGES C CXX)
|
||||
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
|
||||
if(ENABLE_CUDA_DEBUG)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
|
||||
endif()
|
||||
|
||||
# Include directories and libraries
|
||||
include_directories(../../../Common)
|
||||
|
||||
# Source file
|
||||
# Add target for cuSolverSp_LowlevelCholesky
|
||||
add_executable(cuSolverSp_LowlevelCholesky cuSolverSp_LowlevelCholesky.cpp mmio.c mmio_wrapper.cpp)
|
||||
|
||||
target_compile_options(cuSolverSp_LowlevelCholesky PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
|
||||
|
||||
target_compile_features(cuSolverSp_LowlevelCholesky PRIVATE cxx_std_17 cuda_std_17)
|
||||
|
||||
set_target_properties(cuSolverSp_LowlevelCholesky PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
target_include_directories(cuSolverSp_LowlevelCholesky PRIVATE
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(cuSolverSp_LowlevelCholesky PRIVATE
|
||||
CUDA::cudart
|
||||
CUDA::cublas
|
||||
CUDA::cusolver
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverSp_LowlevelCholesky POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap2D_5pt_n100.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Copy data files to output directory
|
||||
add_custom_command(TARGET cuSolverSp_LowlevelCholesky POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lap3D_7pt_n20.mtx
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Include installation configuration
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
|
||||
setup_samples_install()
|
||||
39
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/README.md
Normal file
39
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# cuSolverSp_LowlevelCholesky - cuSolverSp LowlevelCholesky Solver
|
||||
|
||||
## Description
|
||||
|
||||
A CUDA Sample that demonstrates Cholesky factorization using cuSolverSP's low level APIs.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Linear Algebra, CUSOLVER Library
|
||||
|
||||
## Supported SM Architectures
|
||||
|
||||
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
|
||||
|
||||
## Supported OSes
|
||||
|
||||
Linux, Windows
|
||||
|
||||
## Supported CPU Architecture
|
||||
|
||||
x86_64, armv7l
|
||||
|
||||
## CUDA APIs involved
|
||||
|
||||
### [CUDA Driver API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html)
|
||||
cuDoubleComplex, cuComplex
|
||||
|
||||
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
|
||||
cudaMemcpy, cudaStreamDestroy, cudaFree, cudaMalloc, cudaStreamCreate
|
||||
|
||||
## Dependencies needed to build/run
|
||||
[CUSOLVER](../../../README.md#cusolver), [CUSPARSE](../../../README.md#cusparse)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## References (for more details)
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright 2015 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* Please refer to the NVIDIA end user license agreement (EULA) associated
|
||||
* with this source code for terms and conditions that govern your use of
|
||||
* this software. Any use, reproduction, disclosure, or distribution of
|
||||
* this software and related documentation outside the terms of the EULA
|
||||
* is strictly prohibited.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cusolverSp.h"
|
||||
#include "cusolverSp_LOWLEVEL_PREVIEW.h"
|
||||
#include "helper_cuda.h"
|
||||
#include "helper_cusolver.h"
|
||||
|
||||
template <typename T_ELEM>
|
||||
int loadMMSparseMatrix(char *filename,
|
||||
char elem_type,
|
||||
bool csrFormat,
|
||||
int *m,
|
||||
int *n,
|
||||
int *nnz,
|
||||
T_ELEM **aVal,
|
||||
int **aRowInd,
|
||||
int **aColInd,
|
||||
int extendSymMatrix);
|
||||
|
||||
void UsageSP(void)
|
||||
{
|
||||
printf("<options>\n");
|
||||
printf("-h : display this help\n");
|
||||
printf("-file=<filename> : filename containing a matrix in MM format\n");
|
||||
printf("-device=<device_id> : <device_id> if want to run on specific GPU\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void parseCommandLineArguments(int argc, char *argv[], struct testOpts &opts)
|
||||
{
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "-h")) {
|
||||
UsageSP();
|
||||
}
|
||||
|
||||
if (checkCmdLineFlag(argc, (const char **)argv, "file")) {
|
||||
char *fileName = 0;
|
||||
getCmdLineArgumentString(argc, (const char **)argv, "file", &fileName);
|
||||
|
||||
if (fileName) {
|
||||
opts.sparse_mat_filename = fileName;
|
||||
}
|
||||
else {
|
||||
printf("\nIncorrect filename passed to -file \n ");
|
||||
UsageSP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct testOpts opts;
|
||||
cusolverSpHandle_t cusolverSpH = NULL; // reordering, permutation and 1st LU factorization
|
||||
cusparseHandle_t cusparseH = NULL; // residual evaluation
|
||||
cudaStream_t stream = NULL;
|
||||
cusparseMatDescr_t descrA = NULL; // A is a base-0 general matrix
|
||||
|
||||
csrcholInfoHost_t h_info = NULL; // opaque info structure for LU with parital pivoting
|
||||
csrcholInfo_t d_info = NULL; // opaque info structure for LU with parital pivoting
|
||||
|
||||
int rowsA = 0; // number of rows of A
|
||||
int colsA = 0; // number of columns of A
|
||||
int nnzA = 0; // number of nonzeros of A
|
||||
int baseA = 0; // base index in CSR format
|
||||
|
||||
// CSR(A) from I/O
|
||||
int *h_csrRowPtrA = NULL; // <int> n+1
|
||||
int *h_csrColIndA = NULL; // <int> nnzA
|
||||
double *h_csrValA = NULL; // <double> nnzA
|
||||
|
||||
double *h_x = NULL; // <double> n, x = A \ b
|
||||
double *h_b = NULL; // <double> n, b = ones(m,1)
|
||||
double *h_r = NULL; // <double> n, r = b - A*x
|
||||
|
||||
size_t size_internal = 0;
|
||||
size_t size_chol = 0; // size of working space for csrlu
|
||||
void *buffer_cpu = NULL; // working space for Cholesky
|
||||
void *buffer_gpu = NULL; // working space for Cholesky
|
||||
|
||||
int *d_csrRowPtrA = NULL; // <int> n+1
|
||||
int *d_csrColIndA = NULL; // <int> nnzA
|
||||
double *d_csrValA = NULL; // <double> nnzA
|
||||
double *d_x = NULL; // <double> n, x = A \ b
|
||||
double *d_b = NULL; // <double> n, a copy of h_b
|
||||
double *d_r = NULL; // <double> n, r = b - A*x
|
||||
|
||||
// the constants used in residual evaluation, r = b - A*x
|
||||
const double minus_one = -1.0;
|
||||
const double one = 1.0;
|
||||
// the constant used in cusolverSp
|
||||
// singularity is -1 if A is invertible under tol
|
||||
// tol determines the condition of singularity
|
||||
int singularity = 0;
|
||||
const double tol = 1.e-14;
|
||||
|
||||
double x_inf = 0.0; // |x|
|
||||
double r_inf = 0.0; // |r|
|
||||
double A_inf = 0.0; // |A|
|
||||
int errors = 0;
|
||||
|
||||
parseCommandLineArguments(argc, argv, opts);
|
||||
|
||||
findCudaDevice(argc, (const char **)argv);
|
||||
|
||||
if (opts.sparse_mat_filename == NULL) {
|
||||
opts.sparse_mat_filename = sdkFindFilePath("lap2D_5pt_n100.mtx", argv[0]);
|
||||
if (opts.sparse_mat_filename != NULL)
|
||||
printf("Using default input file [%s]\n", opts.sparse_mat_filename);
|
||||
else
|
||||
printf("Could not find lap2D_5pt_n100.mtx\n");
|
||||
}
|
||||
else {
|
||||
printf("Using input file [%s]\n", opts.sparse_mat_filename);
|
||||
}
|
||||
|
||||
printf("step 1: read matrix market format\n");
|
||||
|
||||
if (opts.sparse_mat_filename) {
|
||||
if (loadMMSparseMatrix<double>(opts.sparse_mat_filename,
|
||||
'd',
|
||||
true,
|
||||
&rowsA,
|
||||
&colsA,
|
||||
&nnzA,
|
||||
&h_csrValA,
|
||||
&h_csrRowPtrA,
|
||||
&h_csrColIndA,
|
||||
true)) {
|
||||
return 1;
|
||||
}
|
||||
baseA = h_csrRowPtrA[0]; // baseA = {0,1}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: input matrix is not provided\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rowsA != colsA) {
|
||||
fprintf(stderr, "Error: only support square matrix\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("sparse matrix A is %d x %d with %d nonzeros, base=%d\n", rowsA, colsA, nnzA, baseA);
|
||||
|
||||
checkCudaErrors(cusolverSpCreate(&cusolverSpH));
|
||||
|
||||
checkCudaErrors(cusparseCreate(&cusparseH));
|
||||
|
||||
checkCudaErrors(cudaStreamCreate(&stream));
|
||||
|
||||
checkCudaErrors(cusolverSpSetStream(cusolverSpH, stream));
|
||||
|
||||
checkCudaErrors(cusparseSetStream(cusparseH, stream));
|
||||
|
||||
checkCudaErrors(cusparseCreateMatDescr(&descrA));
|
||||
|
||||
checkCudaErrors(cusparseSetMatType(descrA, CUSPARSE_MATRIX_TYPE_GENERAL));
|
||||
if (baseA) {
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descrA, CUSPARSE_INDEX_BASE_ONE));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cusparseSetMatIndexBase(descrA, CUSPARSE_INDEX_BASE_ZERO));
|
||||
}
|
||||
|
||||
h_x = (double *)malloc(sizeof(double) * colsA);
|
||||
h_b = (double *)malloc(sizeof(double) * rowsA);
|
||||
h_r = (double *)malloc(sizeof(double) * rowsA);
|
||||
|
||||
assert(NULL != h_x);
|
||||
assert(NULL != h_b);
|
||||
assert(NULL != h_r);
|
||||
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrRowPtrA, sizeof(int) * (rowsA + 1)));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrColIndA, sizeof(int) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_csrValA, sizeof(double) * nnzA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_x, sizeof(double) * colsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_b, sizeof(double) * rowsA));
|
||||
checkCudaErrors(cudaMalloc((void **)&d_r, sizeof(double) * rowsA));
|
||||
|
||||
for (int row = 0; row < rowsA; row++) {
|
||||
h_b[row] = 1.0;
|
||||
}
|
||||
|
||||
printf("step 2: create opaque info structure\n");
|
||||
checkCudaErrors(cusolverSpCreateCsrcholInfoHost(&h_info));
|
||||
|
||||
printf("step 3: analyze chol(A) to know structure of L\n");
|
||||
checkCudaErrors(
|
||||
cusolverSpXcsrcholAnalysisHost(cusolverSpH, rowsA, nnzA, descrA, h_csrRowPtrA, h_csrColIndA, h_info));
|
||||
|
||||
printf("step 4: workspace for chol(A)\n");
|
||||
checkCudaErrors(cusolverSpDcsrcholBufferInfoHost(
|
||||
cusolverSpH, rowsA, nnzA, descrA, h_csrValA, h_csrRowPtrA, h_csrColIndA, h_info, &size_internal, &size_chol));
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
buffer_cpu = (void *)malloc(sizeof(char) * size_chol);
|
||||
assert(NULL != buffer_cpu);
|
||||
|
||||
printf("step 5: compute A = L*L^T \n");
|
||||
checkCudaErrors(cusolverSpDcsrcholFactorHost(
|
||||
cusolverSpH, rowsA, nnzA, descrA, h_csrValA, h_csrRowPtrA, h_csrColIndA, h_info, buffer_cpu));
|
||||
|
||||
printf("step 6: check if the matrix is singular \n");
|
||||
checkCudaErrors(cusolverSpDcsrcholZeroPivotHost(cusolverSpH, h_info, tol, &singularity));
|
||||
|
||||
if (0 <= singularity) {
|
||||
fprintf(stderr, "Error: A is not invertible, singularity=%d\n", singularity);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("step 7: solve A*x = b \n");
|
||||
checkCudaErrors(cusolverSpDcsrcholSolveHost(cusolverSpH, rowsA, h_b, h_x, h_info, buffer_cpu));
|
||||
|
||||
printf("step 8: evaluate residual r = b - A*x (result on CPU)\n");
|
||||
// use GPU gemv to compute r = b - A*x
|
||||
checkCudaErrors(cudaMemcpy(d_csrRowPtrA, h_csrRowPtrA, sizeof(int) * (rowsA + 1), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrColIndA, h_csrColIndA, sizeof(int) * nnzA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrValA, h_csrValA, sizeof(double) * nnzA, cudaMemcpyHostToDevice));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(d_r, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_x, h_x, sizeof(double) * colsA, cudaMemcpyHostToDevice));
|
||||
|
||||
/* Wrap raw data into cuSPARSE generic API objects */
|
||||
cusparseSpMatDescr_t matA = NULL;
|
||||
if (baseA) {
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
d_csrRowPtrA,
|
||||
d_csrColIndA,
|
||||
d_csrValA,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ONE,
|
||||
CUDA_R_64F));
|
||||
}
|
||||
else {
|
||||
checkCudaErrors(cusparseCreateCsr(&matA,
|
||||
rowsA,
|
||||
colsA,
|
||||
nnzA,
|
||||
d_csrRowPtrA,
|
||||
d_csrColIndA,
|
||||
d_csrValA,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_BASE_ZERO,
|
||||
CUDA_R_64F));
|
||||
}
|
||||
|
||||
cusparseDnVecDescr_t vecx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecx, colsA, d_x, CUDA_R_64F));
|
||||
cusparseDnVecDescr_t vecAx = NULL;
|
||||
checkCudaErrors(cusparseCreateDnVec(&vecAx, rowsA, d_r, CUDA_R_64F));
|
||||
|
||||
/* Allocate workspace for cuSPARSE */
|
||||
size_t bufferSize = 0;
|
||||
checkCudaErrors(cusparseSpMV_bufferSize(cusparseH,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
&bufferSize));
|
||||
void *buffer = NULL;
|
||||
checkCudaErrors(cudaMalloc(&buffer, bufferSize));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseH,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost));
|
||||
|
||||
x_inf = vec_norminf(colsA, h_x);
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
A_inf = csr_mat_norminf(rowsA, colsA, nnzA, descrA, h_csrValA, h_csrRowPtrA, h_csrColIndA);
|
||||
|
||||
printf("(CPU) |b - A*x| = %E \n", r_inf);
|
||||
printf("(CPU) |A| = %E \n", A_inf);
|
||||
printf("(CPU) |x| = %E \n", x_inf);
|
||||
printf("(CPU) |b - A*x|/(|A|*|x|) = %E \n", r_inf / (A_inf * x_inf));
|
||||
|
||||
printf("step 9: create opaque info structure\n");
|
||||
checkCudaErrors(cusolverSpCreateCsrcholInfo(&d_info));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(d_csrRowPtrA, h_csrRowPtrA, sizeof(int) * (rowsA + 1), cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrColIndA, h_csrColIndA, sizeof(int) * nnzA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_csrValA, h_csrValA, sizeof(double) * nnzA, cudaMemcpyHostToDevice));
|
||||
checkCudaErrors(cudaMemcpy(d_b, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
|
||||
printf("step 10: analyze chol(A) to know structure of L\n");
|
||||
checkCudaErrors(cusolverSpXcsrcholAnalysis(cusolverSpH, rowsA, nnzA, descrA, d_csrRowPtrA, d_csrColIndA, d_info));
|
||||
|
||||
printf("step 11: workspace for chol(A)\n");
|
||||
checkCudaErrors(cusolverSpDcsrcholBufferInfo(
|
||||
cusolverSpH, rowsA, nnzA, descrA, d_csrValA, d_csrRowPtrA, d_csrColIndA, d_info, &size_internal, &size_chol));
|
||||
|
||||
if (buffer_gpu) {
|
||||
checkCudaErrors(cudaFree(buffer_gpu));
|
||||
}
|
||||
checkCudaErrors(cudaMalloc(&buffer_gpu, sizeof(char) * size_chol));
|
||||
|
||||
printf("step 12: compute A = L*L^T \n");
|
||||
checkCudaErrors(cusolverSpDcsrcholFactor(
|
||||
cusolverSpH, rowsA, nnzA, descrA, d_csrValA, d_csrRowPtrA, d_csrColIndA, d_info, buffer_gpu));
|
||||
|
||||
printf("step 13: check if the matrix is singular \n");
|
||||
checkCudaErrors(cusolverSpDcsrcholZeroPivot(cusolverSpH, d_info, tol, &singularity));
|
||||
|
||||
if (0 <= singularity) {
|
||||
fprintf(stderr, "Error: A is not invertible, singularity=%d\n", singularity);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("step 14: solve A*x = b \n");
|
||||
checkCudaErrors(cusolverSpDcsrcholSolve(cusolverSpH, rowsA, d_b, d_x, d_info, buffer_gpu));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(d_r, h_b, sizeof(double) * rowsA, cudaMemcpyHostToDevice));
|
||||
|
||||
checkCudaErrors(cusparseSpMV(cusparseH,
|
||||
CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
&minus_one,
|
||||
matA,
|
||||
vecx,
|
||||
&one,
|
||||
vecAx,
|
||||
CUDA_R_64F,
|
||||
CUSPARSE_SPMV_ALG_DEFAULT,
|
||||
buffer));
|
||||
|
||||
checkCudaErrors(cudaMemcpy(h_r, d_r, sizeof(double) * rowsA, cudaMemcpyDeviceToHost));
|
||||
|
||||
r_inf = vec_norminf(rowsA, h_r);
|
||||
|
||||
printf("(GPU) |b - A*x| = %E \n", r_inf);
|
||||
printf("(GPU) |b - A*x|/(|A|*|x|) = %E \n", r_inf / (A_inf * x_inf));
|
||||
|
||||
|
||||
if (cusolverSpH) {
|
||||
checkCudaErrors(cusolverSpDestroy(cusolverSpH));
|
||||
}
|
||||
if (cusparseH) {
|
||||
checkCudaErrors(cusparseDestroy(cusparseH));
|
||||
}
|
||||
if (stream) {
|
||||
checkCudaErrors(cudaStreamDestroy(stream));
|
||||
}
|
||||
if (descrA) {
|
||||
checkCudaErrors(cusparseDestroyMatDescr(descrA));
|
||||
}
|
||||
if (h_info) {
|
||||
checkCudaErrors(cusolverSpDestroyCsrcholInfoHost(h_info));
|
||||
}
|
||||
if (d_info) {
|
||||
checkCudaErrors(cusolverSpDestroyCsrcholInfo(d_info));
|
||||
}
|
||||
if (matA) {
|
||||
checkCudaErrors(cusparseDestroySpMat(matA));
|
||||
}
|
||||
if (vecx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecx));
|
||||
}
|
||||
if (vecAx) {
|
||||
checkCudaErrors(cusparseDestroyDnVec(vecAx));
|
||||
}
|
||||
|
||||
if (h_csrValA) {
|
||||
free(h_csrValA);
|
||||
}
|
||||
if (h_csrRowPtrA) {
|
||||
free(h_csrRowPtrA);
|
||||
}
|
||||
if (h_csrColIndA) {
|
||||
free(h_csrColIndA);
|
||||
}
|
||||
|
||||
if (h_x) {
|
||||
free(h_x);
|
||||
}
|
||||
if (h_b) {
|
||||
free(h_b);
|
||||
}
|
||||
if (h_r) {
|
||||
free(h_r);
|
||||
}
|
||||
|
||||
if (buffer_cpu) {
|
||||
free(buffer_cpu);
|
||||
}
|
||||
if (buffer_gpu) {
|
||||
checkCudaErrors(cudaFree(buffer_gpu));
|
||||
}
|
||||
|
||||
if (d_csrValA) {
|
||||
checkCudaErrors(cudaFree(d_csrValA));
|
||||
}
|
||||
if (d_csrRowPtrA) {
|
||||
checkCudaErrors(cudaFree(d_csrRowPtrA));
|
||||
}
|
||||
if (d_csrColIndA) {
|
||||
checkCudaErrors(cudaFree(d_csrColIndA));
|
||||
}
|
||||
if (d_x) {
|
||||
checkCudaErrors(cudaFree(d_x));
|
||||
}
|
||||
if (d_b) {
|
||||
checkCudaErrors(cudaFree(d_b));
|
||||
}
|
||||
if (d_r) {
|
||||
checkCudaErrors(cudaFree(d_r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
29803
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/lap2D_5pt_n100.mtx
Normal file
29803
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/lap2D_5pt_n100.mtx
Normal file
File diff suppressed because it is too large
Load Diff
30803
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/lap3D_7pt_n20.mtx
Normal file
30803
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/lap3D_7pt_n20.mtx
Normal file
File diff suppressed because it is too large
Load Diff
477
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/mmio.c
Normal file
477
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/mmio.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* avoid Windows warnings (for example: strcpy, fscanf, etc.) */
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_)
|
||||
{
|
||||
FILE *f;
|
||||
MM_typecode matcode;
|
||||
int M, N, nz;
|
||||
int i;
|
||||
double *val;
|
||||
int *I, *J;
|
||||
|
||||
if ((f = fopen(fname, "r")) == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
if (mm_read_banner(f, &matcode) != 0) {
|
||||
printf("mm_read_unsymetric: Could not process Matrix Market banner ");
|
||||
printf(" in file [%s]\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!(mm_is_real(matcode) && mm_is_matrix(matcode) && mm_is_sparse(matcode))) {
|
||||
fprintf(stderr, "Sorry, this application does not support ");
|
||||
fprintf(stderr, "Market Market type: [%s]\n", mm_typecode_to_str(matcode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find out size of sparse matrix: M, N, nz .... */
|
||||
|
||||
if (mm_read_mtx_crd_size(f, &M, &N, &nz) != 0) {
|
||||
fprintf(stderr, "read_unsymmetric_sparse(): could not parse matrix size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*M_ = M;
|
||||
*N_ = N;
|
||||
*nz_ = nz;
|
||||
|
||||
/* reserve memory for matrices */
|
||||
|
||||
I = (int *)malloc(nz * sizeof(int));
|
||||
J = (int *)malloc(nz * sizeof(int));
|
||||
val = (double *)malloc(nz * sizeof(double));
|
||||
|
||||
*val_ = val;
|
||||
*I_ = I;
|
||||
*J_ = J;
|
||||
|
||||
/* NOTE: when reading in doubles, ANSI C requires the use of the "l" */
|
||||
/* specifier as in "%lg", "%lf", "%le", otherwise errors will occur */
|
||||
/* (ANSI C X3.159-1989, Sec. 4.9.6.2, p. 136 lines 13-15) */
|
||||
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3) {
|
||||
return -1;
|
||||
}
|
||||
I[i]--; /* adjust from 1-based to 0-based */
|
||||
J[i]--;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_is_valid(MM_typecode matcode)
|
||||
{
|
||||
if (!mm_is_matrix(matcode))
|
||||
return 0;
|
||||
if (mm_is_dense(matcode) && mm_is_pattern(matcode))
|
||||
return 0;
|
||||
if (mm_is_real(matcode) && mm_is_hermitian(matcode))
|
||||
return 0;
|
||||
if (mm_is_pattern(matcode) && (mm_is_hermitian(matcode) || mm_is_skew(matcode)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
char banner[MM_MAX_TOKEN_LENGTH];
|
||||
char mtx[MM_MAX_TOKEN_LENGTH];
|
||||
char crd[MM_MAX_TOKEN_LENGTH];
|
||||
char data_type[MM_MAX_TOKEN_LENGTH];
|
||||
char storage_scheme[MM_MAX_TOKEN_LENGTH];
|
||||
char *p;
|
||||
|
||||
|
||||
mm_clear_typecode(matcode);
|
||||
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type, storage_scheme) != 5)
|
||||
return MM_PREMATURE_EOF;
|
||||
|
||||
for (p = mtx; *p != '\0'; *p = tolower(*p), p++)
|
||||
; /* convert to lower case */
|
||||
for (p = crd; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = data_type; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
for (p = storage_scheme; *p != '\0'; *p = tolower(*p), p++)
|
||||
;
|
||||
|
||||
/* check for banner */
|
||||
if (strncmp(banner, MatrixMarketBanner, strlen(MatrixMarketBanner)) != 0)
|
||||
return MM_NO_HEADER;
|
||||
|
||||
/* first field should be "mtx" */
|
||||
if (strcmp(mtx, MM_MTX_STR) != 0)
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
mm_set_matrix(matcode);
|
||||
|
||||
|
||||
/* second field describes whether this is a sparse matrix (in coordinate
|
||||
storage) or a dense array */
|
||||
|
||||
|
||||
if (strcmp(crd, MM_SPARSE_STR) == 0)
|
||||
mm_set_sparse(matcode);
|
||||
else if (strcmp(crd, MM_DENSE_STR) == 0)
|
||||
mm_set_dense(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
/* third field */
|
||||
|
||||
if (strcmp(data_type, MM_REAL_STR) == 0)
|
||||
mm_set_real(matcode);
|
||||
else if (strcmp(data_type, MM_COMPLEX_STR) == 0)
|
||||
mm_set_complex(matcode);
|
||||
else if (strcmp(data_type, MM_PATTERN_STR) == 0)
|
||||
mm_set_pattern(matcode);
|
||||
else if (strcmp(data_type, MM_INT_STR) == 0)
|
||||
mm_set_integer(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
/* fourth field */
|
||||
|
||||
if (strcmp(storage_scheme, MM_GENERAL_STR) == 0)
|
||||
mm_set_general(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SYMM_STR) == 0)
|
||||
mm_set_symmetric(matcode);
|
||||
else if (strcmp(storage_scheme, MM_HERM_STR) == 0)
|
||||
mm_set_hermitian(matcode);
|
||||
else if (strcmp(storage_scheme, MM_SKEW_STR) == 0)
|
||||
mm_set_skew(matcode);
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz)
|
||||
{
|
||||
if (fprintf(f, "%d %d %d\n", M, N, nz) != 3)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = *nz = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d %d", M, N, nz) == 3)
|
||||
return 0;
|
||||
|
||||
else
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d %d", M, N, nz);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N)
|
||||
{
|
||||
char line[MM_MAX_LINE_LENGTH];
|
||||
int num_items_read;
|
||||
/* set return null parameter values, in case we exit with errors */
|
||||
*M = *N = 0;
|
||||
|
||||
/* now continue scanning until you reach the end-of-comments */
|
||||
do {
|
||||
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (line[0] == '%');
|
||||
|
||||
/* line[] is either blank or has M,N, nz */
|
||||
if (sscanf(line, "%d %d", M, N) == 2)
|
||||
return 0;
|
||||
|
||||
else /* we have a blank line */
|
||||
do {
|
||||
num_items_read = fscanf(f, "%d %d", M, N);
|
||||
if (num_items_read == EOF)
|
||||
return MM_PREMATURE_EOF;
|
||||
} while (num_items_read != 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N)
|
||||
{
|
||||
if (fprintf(f, "%d %d\n", M, N) != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/******************************************************************/
|
||||
/* use when I[], J[], and val[]J, and val[] are already allocated */
|
||||
/******************************************************************/
|
||||
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
int i;
|
||||
if (mm_is_complex(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d %lg %lg", &I[i], &J[i], &val[2 * i], &val[2 * i + 1]) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
for (i = 0; i < nz; i++) {
|
||||
if (fscanf(f, "%d %d %lg\n", &I[i], &J[i], &val[i]) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
for (i = 0; i < nz; i++)
|
||||
if (fscanf(f, "%d %d", &I[i], &J[i]) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *imag, MM_typecode matcode)
|
||||
{
|
||||
if (mm_is_complex(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg %lg", I, J, real, imag) != 4)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else if (mm_is_real(matcode) || mm_is_integer(matcode)) {
|
||||
if (fscanf(f, "%d %d %lg\n", I, J, real) != 3)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(matcode)) {
|
||||
if (fscanf(f, "%d %d", I, J) != 2)
|
||||
return MM_PREMATURE_EOF;
|
||||
}
|
||||
else
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
mm_read_mtx_crd() fills M, N, nz, array of values, and return
|
||||
type code, e.g. 'MCRS'
|
||||
|
||||
if matrix is complex, values[] is of size 2*nz,
|
||||
(nz pairs of real/imaginary values)
|
||||
************************************************************************/
|
||||
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode)
|
||||
{
|
||||
int ret_code;
|
||||
FILE *f;
|
||||
|
||||
if (strcmp(fname, "stdin") == 0)
|
||||
f = stdin;
|
||||
else if ((f = fopen(fname, "r")) == NULL)
|
||||
return MM_COULD_NOT_READ_FILE;
|
||||
|
||||
|
||||
if ((ret_code = mm_read_banner(f, matcode)) != 0)
|
||||
return ret_code;
|
||||
|
||||
if (!(mm_is_valid(*matcode) && mm_is_sparse(*matcode) && mm_is_matrix(*matcode)))
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
|
||||
if ((ret_code = mm_read_mtx_crd_size(f, M, N, nz)) != 0)
|
||||
return ret_code;
|
||||
|
||||
|
||||
*I = (int *)malloc(*nz * sizeof(int));
|
||||
*J = (int *)malloc(*nz * sizeof(int));
|
||||
*val = NULL;
|
||||
|
||||
if (mm_is_complex(*matcode)) {
|
||||
*val = (double *)malloc(*nz * 2 * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
else if (mm_is_real(*matcode) || mm_is_integer(*matcode)) {
|
||||
*val = (double *)malloc(*nz * sizeof(double));
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else if (mm_is_pattern(*matcode)) {
|
||||
ret_code = mm_read_mtx_crd_data(f, *M, *N, *nz, *I, *J, *val, *matcode);
|
||||
if (ret_code != 0)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
if (f != stdin)
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode)
|
||||
{
|
||||
char *str = mm_typecode_to_str(matcode);
|
||||
int ret_code;
|
||||
|
||||
ret_code = fprintf(f, "%s %s\n", MatrixMarketBanner, str);
|
||||
free(str);
|
||||
if (ret_code != 2)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
if (strcmp(fname, "stdout") == 0)
|
||||
f = stdout;
|
||||
else if ((f = fopen(fname, "w")) == NULL)
|
||||
return MM_COULD_NOT_WRITE_FILE;
|
||||
|
||||
/* print banner followed by typecode */
|
||||
fprintf(f, "%s ", MatrixMarketBanner);
|
||||
fprintf(f, "%s\n", mm_typecode_to_str(matcode));
|
||||
|
||||
/* print matrix sizes and nonzeros */
|
||||
fprintf(f, "%d %d %d\n", M, N, nz);
|
||||
|
||||
/* print values */
|
||||
if (mm_is_pattern(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d\n", I[i], J[i]);
|
||||
else if (mm_is_integer(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %d\n", I[i], J[i], (int)val[i]);
|
||||
else if (mm_is_real(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g\n", I[i], J[i], val[i]);
|
||||
else if (mm_is_complex(matcode))
|
||||
for (i = 0; i < nz; i++)
|
||||
fprintf(f, "%d %d %20.16g %20.16g\n", I[i], J[i], val[2 * i], val[2 * i + 1]);
|
||||
else {
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
return MM_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
if (f != stdout)
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new copy of a string s. mm_strdup() is a common routine, but
|
||||
* not part of ANSI C, so it is included here. Used by mm_typecode_to_str().
|
||||
*
|
||||
*/
|
||||
static char *mm_strdup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *s2 = (char *)malloc((len + 1) * sizeof(char));
|
||||
return strcpy(s2, s);
|
||||
}
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode)
|
||||
{
|
||||
char buffer[MM_MAX_LINE_LENGTH];
|
||||
char *types[4];
|
||||
// char *mm_strdup(const char *);
|
||||
// int error =0;
|
||||
|
||||
/* check for MTX type */
|
||||
if (mm_is_matrix(matcode))
|
||||
types[0] = MM_MTX_STR;
|
||||
else
|
||||
return NULL; // error=1;
|
||||
|
||||
/* check for CRD or ARR matrix */
|
||||
if (mm_is_sparse(matcode))
|
||||
types[1] = MM_SPARSE_STR;
|
||||
else if (mm_is_dense(matcode))
|
||||
types[1] = MM_DENSE_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* check for element data type */
|
||||
if (mm_is_real(matcode))
|
||||
types[2] = MM_REAL_STR;
|
||||
else if (mm_is_complex(matcode))
|
||||
types[2] = MM_COMPLEX_STR;
|
||||
else if (mm_is_pattern(matcode))
|
||||
types[2] = MM_PATTERN_STR;
|
||||
else if (mm_is_integer(matcode))
|
||||
types[2] = MM_INT_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
|
||||
/* check for symmetry type */
|
||||
if (mm_is_general(matcode))
|
||||
types[3] = MM_GENERAL_STR;
|
||||
else if (mm_is_symmetric(matcode))
|
||||
types[3] = MM_SYMM_STR;
|
||||
else if (mm_is_hermitian(matcode))
|
||||
types[3] = MM_HERM_STR;
|
||||
else if (mm_is_skew(matcode))
|
||||
types[3] = MM_SKEW_STR;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
sprintf(buffer, "%s %s %s %s", types[0], types[1], types[2], types[3]);
|
||||
return mm_strdup(buffer);
|
||||
}
|
||||
138
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/mmio.h
Normal file
138
cpp/4_CUDA_Libraries/cuSolverSp_LowlevelCholesky/mmio.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Matrix Market I/O library for ANSI C
|
||||
*
|
||||
* See http://math.nist.gov/MatrixMarket for details.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MM_IO_H
|
||||
#define MM_IO_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define MM_MAX_LINE_LENGTH 1025
|
||||
#define MatrixMarketBanner "%%MatrixMarket"
|
||||
#define MM_MAX_TOKEN_LENGTH 64
|
||||
|
||||
typedef char MM_typecode[4];
|
||||
|
||||
char *mm_typecode_to_str(MM_typecode matcode);
|
||||
|
||||
int mm_read_banner(FILE *f, MM_typecode *matcode);
|
||||
int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz);
|
||||
int mm_read_mtx_array_size(FILE *f, int *M, int *N);
|
||||
|
||||
int mm_write_banner(FILE *f, MM_typecode matcode);
|
||||
int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz);
|
||||
int mm_write_mtx_array_size(FILE *f, int M, int N);
|
||||
|
||||
|
||||
/********************* MM_typecode query fucntions ***************************/
|
||||
|
||||
#define mm_is_matrix(typecode) ((typecode)[0] == 'M')
|
||||
|
||||
#define mm_is_sparse(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_coordinate(typecode) ((typecode)[1] == 'C')
|
||||
#define mm_is_dense(typecode) ((typecode)[1] == 'A')
|
||||
#define mm_is_array(typecode) ((typecode)[1] == 'A')
|
||||
|
||||
#define mm_is_complex(typecode) ((typecode)[2] == 'C')
|
||||
#define mm_is_real(typecode) ((typecode)[2] == 'R')
|
||||
#define mm_is_pattern(typecode) ((typecode)[2] == 'P')
|
||||
#define mm_is_integer(typecode) ((typecode)[2] == 'I')
|
||||
|
||||
#define mm_is_symmetric(typecode) ((typecode)[3] == 'S')
|
||||
#define mm_is_general(typecode) ((typecode)[3] == 'G')
|
||||
#define mm_is_skew(typecode) ((typecode)[3] == 'K')
|
||||
#define mm_is_hermitian(typecode) ((typecode)[3] == 'H')
|
||||
|
||||
int mm_is_valid(MM_typecode matcode); /* too complex for a macro */
|
||||
|
||||
|
||||
/********************* MM_typecode modify fucntions ***************************/
|
||||
|
||||
#define mm_set_matrix(typecode) ((*typecode)[0] = 'M')
|
||||
#define mm_set_coordinate(typecode) ((*typecode)[1] = 'C')
|
||||
#define mm_set_array(typecode) ((*typecode)[1] = 'A')
|
||||
#define mm_set_dense(typecode) mm_set_array(typecode)
|
||||
#define mm_set_sparse(typecode) mm_set_coordinate(typecode)
|
||||
|
||||
#define mm_set_complex(typecode) ((*typecode)[2] = 'C')
|
||||
#define mm_set_real(typecode) ((*typecode)[2] = 'R')
|
||||
#define mm_set_pattern(typecode) ((*typecode)[2] = 'P')
|
||||
#define mm_set_integer(typecode) ((*typecode)[2] = 'I')
|
||||
|
||||
|
||||
#define mm_set_symmetric(typecode) ((*typecode)[3] = 'S')
|
||||
#define mm_set_general(typecode) ((*typecode)[3] = 'G')
|
||||
#define mm_set_skew(typecode) ((*typecode)[3] = 'K')
|
||||
#define mm_set_hermitian(typecode) ((*typecode)[3] = 'H')
|
||||
|
||||
#define mm_clear_typecode(typecode) ((*typecode)[0] = (*typecode)[1] = (*typecode)[2] = ' ', (*typecode)[3] = 'G')
|
||||
|
||||
#define mm_initialize_typecode(typecode) mm_clear_typecode(typecode)
|
||||
|
||||
|
||||
/********************* Matrix Market error codes ***************************/
|
||||
|
||||
|
||||
#define MM_COULD_NOT_READ_FILE 11
|
||||
#define MM_PREMATURE_EOF 12
|
||||
#define MM_NOT_MTX 13
|
||||
#define MM_NO_HEADER 14
|
||||
#define MM_UNSUPPORTED_TYPE 15
|
||||
#define MM_LINE_TOO_LONG 16
|
||||
#define MM_COULD_NOT_WRITE_FILE 17
|
||||
|
||||
|
||||
/******************** Matrix Market internal definitions ********************
|
||||
|
||||
MM_matrix_typecode: 4-character sequence
|
||||
|
||||
ojbect sparse/ data storage
|
||||
dense type scheme
|
||||
|
||||
string position: [0] [1] [2] [3]
|
||||
|
||||
Matrix typecode: M(atrix) C(oord) R(eal) G(eneral)
|
||||
A(array) C(omplex) H(ermitian)
|
||||
P(attern) S(ymmetric)
|
||||
I(nteger) K(kew)
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#define MM_MTX_STR "matrix"
|
||||
#define MM_ARRAY_STR "array"
|
||||
#define MM_DENSE_STR "array"
|
||||
#define MM_COORDINATE_STR "coordinate"
|
||||
#define MM_SPARSE_STR "coordinate"
|
||||
#define MM_COMPLEX_STR "complex"
|
||||
#define MM_REAL_STR "real"
|
||||
#define MM_INT_STR "integer"
|
||||
#define MM_GENERAL_STR "general"
|
||||
#define MM_SYMM_STR "symmetric"
|
||||
#define MM_HERM_STR "hermitian"
|
||||
#define MM_SKEW_STR "skew-symmetric"
|
||||
#define MM_PATTERN_STR "pattern"
|
||||
|
||||
|
||||
/* high level routines */
|
||||
int mm_read_mtx_crd(char *fname, int *M, int *N, int *nz, int **I, int **J, double **val, MM_typecode *matcode);
|
||||
|
||||
int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode);
|
||||
int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *img, MM_typecode matcode);
|
||||
|
||||
int mm_read_unsymmetric_sparse(const char *fname, int *M_, int *N_, int *nz_, double **val_, int **I_, int **J_);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user