From 43d7a794ab1df72e436efd5c32948e6be8879009 Mon Sep 17 00:00:00 2001 From: Matthew Johnson Date: Sat, 16 May 2015 13:26:46 +0100 Subject: [PATCH] Further cleanup of exceptions, added API specific exception classes and created a shared struct for passing exception information. --- src/NativeProviders/CUDA/blas.cpp | 96 +++++++++------ src/NativeProviders/CUDA/capabilities.cpp | 25 +++- src/NativeProviders/CUDA/wrapper_cuda.h | 9 ++ src/Numerics/Numerics.csproj | 4 + .../LinearAlgebra/Cuda/CuSolverException.cs | 110 ++++++++++++++++++ .../LinearAlgebra/Cuda/CublasException.cs | 102 ++++++++++++++++ .../LinearAlgebra/Cuda/CudaException.cs | 90 ++++++++++++++ .../Cuda/CudaLinearAlgebraProvider.cs | 96 ++------------- .../LinearAlgebra/Cuda/CudaResults.cs | 58 +++++++++ .../LinearAlgebra/Cuda/SafeNativeMethods.cs | 40 +++---- 10 files changed, 488 insertions(+), 142 deletions(-) create mode 100644 src/Numerics/Providers/LinearAlgebra/Cuda/CuSolverException.cs create mode 100644 src/Numerics/Providers/LinearAlgebra/Cuda/CublasException.cs create mode 100644 src/Numerics/Providers/LinearAlgebra/Cuda/CudaException.cs create mode 100644 src/Numerics/Providers/LinearAlgebra/Cuda/CudaResults.cs diff --git a/src/NativeProviders/CUDA/blas.cpp b/src/NativeProviders/CUDA/blas.cpp index 76c67646..e9e363e8 100644 --- a/src/NativeProviders/CUDA/blas.cpp +++ b/src/NativeProviders/CUDA/blas.cpp @@ -93,88 +93,112 @@ exit: extern "C" { - DLLEXPORT void s_axpy(const cublasHandle_t blasHandle, const int n, const float alpha, const float x[], float y[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasSaxpy, error, blasStatus); + DLLEXPORT CudaResults s_axpy(const cublasHandle_t blasHandle, const int n, const float alpha, const float x[], float y[]){ + CudaResults ret; + cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasSaxpy, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void d_axpy(const cublasHandle_t blasHandle, const int n, const double alpha, const double x[], double y[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasDaxpy, error, blasStatus); + DLLEXPORT CudaResults d_axpy(const cublasHandle_t blasHandle, const int n, const double alpha, const double x[], double y[]){ + CudaResults ret; + cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasDaxpy, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void c_axpy(const cublasHandle_t blasHandle, const int n, const cuComplex alpha, const cuComplex x[], cuComplex y[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasCaxpy, error, blasStatus); + DLLEXPORT CudaResults c_axpy(const cublasHandle_t blasHandle, const int n, const cuComplex alpha, const cuComplex x[], cuComplex y[]){ + CudaResults ret; + cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasCaxpy, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void z_axpy(const cublasHandle_t blasHandle, const int n, const cuDoubleComplex alpha, const cuDoubleComplex x[], cuDoubleComplex y[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasZaxpy, error, blasStatus); + DLLEXPORT CudaResults z_axpy(const cublasHandle_t blasHandle, const int n, const cuDoubleComplex alpha, const cuDoubleComplex x[], cuDoubleComplex y[]){ + CudaResults ret; + cuda_axpy(blasHandle, n, alpha, x, 1, y, 1, cublasZaxpy, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void s_scale(const cublasHandle_t blasHandle, const int n, const float alpha, float x[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_scal(blasHandle, n, alpha, x, 1, cublasSscal, error, blasStatus); + DLLEXPORT CudaResults s_scale(const cublasHandle_t blasHandle, const int n, const float alpha, float x[]){ + CudaResults ret; + cuda_scal(blasHandle, n, alpha, x, 1, cublasSscal, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void d_scale(const cublasHandle_t blasHandle, const int n, const double alpha, double x[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_scal(blasHandle, n, alpha, x, 1, cublasDscal, error, blasStatus); + DLLEXPORT CudaResults d_scale(const cublasHandle_t blasHandle, const int n, const double alpha, double x[]){ + CudaResults ret; + cuda_scal(blasHandle, n, alpha, x, 1, cublasDscal, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void c_scale(const cublasHandle_t blasHandle, const int n, const cuComplex alpha, cuComplex x[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_scal(blasHandle, n, alpha, x, 1, cublasCscal, error, blasStatus); + DLLEXPORT CudaResults c_scale(const cublasHandle_t blasHandle, const int n, const cuComplex alpha, cuComplex x[]){ + CudaResults ret; + cuda_scal(blasHandle, n, alpha, x, 1, cublasCscal, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void z_scale(const cublasHandle_t blasHandle, const int n, const cuDoubleComplex alpha, cuDoubleComplex x[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuda_scal(blasHandle, n, alpha, x, 1, cublasZscal, error, blasStatus); + DLLEXPORT CudaResults z_scale(const cublasHandle_t blasHandle, const int n, const cuDoubleComplex alpha, cuDoubleComplex x[]){ + CudaResults ret; + cuda_scal(blasHandle, n, alpha, x, 1, cublasZscal, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT float s_dot_product(const cublasHandle_t blasHandle, const int n, const float x[], const float y[], cudaError_t *error, cublasStatus_t *blasStatus){ - float ret; - cuda_dot(blasHandle, n, x, 1, y, 1, &ret, cublasSdot, error, blasStatus); + DLLEXPORT CudaResults s_dot_product(const cublasHandle_t blasHandle, const int n, const float x[], const float y[], float *result){ + CudaResults ret; + cuda_dot(blasHandle, n, x, 1, y, 1, result, cublasSdot, &ret.error, &ret.blasStatus); return ret; } - DLLEXPORT double d_dot_product(const cublasHandle_t blasHandle, const int n, const double x[], const double y[], cudaError_t *error, cublasStatus_t *blasStatus){ - double ret; - cuda_dot(blasHandle, n, x, 1, y, 1, &ret, cublasDdot, error, blasStatus); + DLLEXPORT CudaResults d_dot_product(const cublasHandle_t blasHandle, const int n, const double x[], const double y[], double *result){ + CudaResults ret; + cuda_dot(blasHandle, n, x, 1, y, 1, result, cublasDdot, &ret.error, &ret.blasStatus); return ret; } - DLLEXPORT cuComplex c_dot_product(const cublasHandle_t blasHandle, const int n, const cuComplex x[], const cuComplex y[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuComplex ret; - cuda_dot(blasHandle, n, x, 1, y, 1, &ret, cublasCdotu, error, blasStatus); + DLLEXPORT CudaResults c_dot_product(const cublasHandle_t blasHandle, const int n, const cuComplex x[], const cuComplex y[], cuComplex *result){ + CudaResults ret; + cuda_dot(blasHandle, n, x, 1, y, 1, result, cublasCdotu, &ret.error, &ret.blasStatus); return ret; } - DLLEXPORT cuDoubleComplex z_dot_product(const cublasHandle_t blasHandle, const int n, const cuDoubleComplex x[], const cuDoubleComplex y[], cudaError_t *error, cublasStatus_t *blasStatus){ - cuDoubleComplex ret; - cuda_dot(blasHandle, n, x, 1, y, 1, &ret, cublasZdotu, error, blasStatus); + DLLEXPORT CudaResults z_dot_product(const cublasHandle_t blasHandle, const int n, const cuDoubleComplex x[], const cuDoubleComplex y[], cuDoubleComplex *result){ + CudaResults ret; + cuda_dot(blasHandle, n, x, 1, y, 1, result, cublasZdotu, &ret.error, &ret.blasStatus); return ret; } - DLLEXPORT void s_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const float alpha, const float x[], const float y[], const float beta, float c[], cudaError_t *error, cublasStatus_t *blasStatus){ + DLLEXPORT CudaResults s_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const float alpha, const float x[], const float y[], const float beta, float c[]){ + CudaResults ret; int lda = transA == CUBLAS_OP_N ? m : k; int ldb = transB == CUBLAS_OP_N ? k : n; - cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasSgemm, error, blasStatus); + cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasSgemm, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void d_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const double alpha, const double x[], const double y[], const double beta, double c[], cudaError_t *error, cublasStatus_t *blasStatus){ + DLLEXPORT CudaResults d_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const double alpha, const double x[], const double y[], const double beta, double c[]){ + CudaResults ret; int lda = transA == CUBLAS_OP_N ? m : k; int ldb = transB == CUBLAS_OP_N ? k : n; - cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasDgemm, error, blasStatus); + cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasDgemm, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void c_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const cuComplex alpha, const cuComplex x[], const cuComplex y[], const cuComplex beta, cuComplex c[], cudaError_t *error, cublasStatus_t *blasStatus){ + DLLEXPORT CudaResults c_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const cuComplex alpha, const cuComplex x[], const cuComplex y[], const cuComplex beta, cuComplex c[]){ + CudaResults ret; int lda = transA == CUBLAS_OP_N ? m : k; int ldb = transB == CUBLAS_OP_N ? k : n; - cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasCgemm, error, blasStatus); + cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasCgemm, &ret.error, &ret.blasStatus); + return ret; } - DLLEXPORT void z_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const cuDoubleComplex alpha, const cuDoubleComplex x[], const cuDoubleComplex y[], const cuDoubleComplex beta, cuDoubleComplex c[], cudaError_t *error, cublasStatus_t *blasStatus){ + DLLEXPORT CudaResults z_matrix_multiply(const cublasHandle_t blasHandle, cublasOperation_t transA, cublasOperation_t transB, const int m, const int n, const int k, const cuDoubleComplex alpha, const cuDoubleComplex x[], const cuDoubleComplex y[], const cuDoubleComplex beta, cuDoubleComplex c[]){ + CudaResults ret; int lda = transA == CUBLAS_OP_N ? m : k; int ldb = transB == CUBLAS_OP_N ? k : n; - cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasZgemm, error, blasStatus); + cuda_gemm(blasHandle, transA, transB, m, n, k, alpha, x, lda, y, ldb, beta, c, m, cublasZgemm, &ret.error, &ret.blasStatus); + return ret; } } diff --git a/src/NativeProviders/CUDA/capabilities.cpp b/src/NativeProviders/CUDA/capabilities.cpp index 75fb1d59..aa922577 100644 --- a/src/NativeProviders/CUDA/capabilities.cpp +++ b/src/NativeProviders/CUDA/capabilities.cpp @@ -1,4 +1,5 @@ #include "wrapper_common.h" +#include "cuda_runtime.h" #include "cublas_v2.h" #include "cusolverDn.h" @@ -14,6 +15,24 @@ extern "C" { */ DLLEXPORT int query_capability(const int capability) { + int count; + int device; + cudaDeviceProp prop; + + if (cudaGetDeviceCount(&count)) + return 0; + + if (count == 0) + return 0; + + if (cudaGetDevice(&device)) + return 0; + + if (cudaGetDeviceProperties(&prop, device)) + return 0; + + + switch (capability) { @@ -42,10 +61,12 @@ extern "C" { #endif // COMMON/SHARED - case 64: return 1; // revision + case 64: + return prop.major; // LINEAR ALGEBRA - case 128: return 1; // basic dense linear algebra + case 128: + return prop.major >= 2; // OPTIMIZATION case 256: return 0; // basic optimization diff --git a/src/NativeProviders/CUDA/wrapper_cuda.h b/src/NativeProviders/CUDA/wrapper_cuda.h index cd25b753..d20d15b2 100644 --- a/src/NativeProviders/CUDA/wrapper_cuda.h +++ b/src/NativeProviders/CUDA/wrapper_cuda.h @@ -2,7 +2,16 @@ #define WRAPPER_CUDA_H #include "wrapper_common.h" +#include "cuda_runtime.h" +#include "cusolver_common.h" #define SAFECUDACALL(error,call) {*error = call; if(*error){goto exit;}} +typedef struct +{ + cudaError_t error; + cublasStatus_t blasStatus; + cusolverStatus_t solverStatus; +} CudaResults; + #endif \ No newline at end of file diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 941675c9..80547e09 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -159,12 +159,16 @@ + + + + diff --git a/src/Numerics/Providers/LinearAlgebra/Cuda/CuSolverException.cs b/src/Numerics/Providers/LinearAlgebra/Cuda/CuSolverException.cs new file mode 100644 index 00000000..b1617ee3 --- /dev/null +++ b/src/Numerics/Providers/LinearAlgebra/Cuda/CuSolverException.cs @@ -0,0 +1,110 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2013 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda +{ + /// + /// Exceptions thrown by the cuSolverDn API. + /// + public class CuSolverException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The status code returned from the API + public CuSolverException(int statusCode) + : base(CuSolverException.GetErrorMessage(statusCode)) + { + this.StatusCode = statusCode; + } + + /// + /// Gets the status code returned by the cuSolverDn API + /// + public int StatusCode { get; private set; } + + /// + /// Returns the appropriate error message for each status code. + /// + /// The status code returned from the API + /// The corresponding error message + private static string GetErrorMessage(int statusCode) + { + switch (statusCode) + { + case 0: // CUSOLVER_STATUS_SUCCESS + return "The operation completed successfully."; + + case 1: // CUSOLVER_STATUS_NOT_INITIALIZED + return "The cuSolver library was not initialized. This is usually caused by the lack of a prior call, an error in the CUDA Runtime API called by the cuSolver routine, or an error in the hardware setup."; + + case 2: // CUSOLVER_STATUS_ALLOC_FAILED + return "Resource allocation failed inside the cuSolver library. This is usually caused by a cudaMalloc() failure."; + + case 3: // CUSOLVER_STATUS_INVALID_VALUE + return "An unsupported value or parameter was passed to the function (a negative vector size, for example)."; + + case 4: // CUSOLVER_STATUS_ARCH_MISMATCH + return "The function requires a feature absent from the device architecture; usually caused by the lack of support for atomic operations or double precision."; + + case 5: // CUSOLVER_STATUS_MAPPING_ERROR + return "Mapping Error"; + + case 6: // CUSOLVER_STATUS_EXECUTION_FAILED + return "The GPU program failed to execute. This is often caused by a launch failure of the kernel on the GPU, which can be caused by multiple reasons."; + + case 7: //CUSOLVER_STATUS_INTERNAL_ERROR + return "An internal cuSolver operation failed. This error is usually caused by a cudaMemcpyAsync() failure."; + + case 8: // CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED + return "The matrix type is not supported by this function. This is usually caused by passing an invalid matrix descriptor to the function."; + + case 9: // CUSOLVER_STATUS_NOT_SUPPORTED + return "The functionality requested is not supported"; + + case 10: // CUSOLVER_STATUS_ZERO_PIVOT + return "Zero Pivot"; + + case 11: //CUSOLVER_STATUS_INVALID_LICENSE + return "The functionality requested requires some license and an error was detected when trying to check the current licensing. This error can happen if the license is not present or is expired or if the environment variable NVIDIA_LICENSE_FILE is not set properly."; + + default: + throw new Exception("Unrecognized cuSolverDn status code"); + + + } + } + } +} diff --git a/src/Numerics/Providers/LinearAlgebra/Cuda/CublasException.cs b/src/Numerics/Providers/LinearAlgebra/Cuda/CublasException.cs new file mode 100644 index 00000000..1d51f603 --- /dev/null +++ b/src/Numerics/Providers/LinearAlgebra/Cuda/CublasException.cs @@ -0,0 +1,102 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2013 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda +{ + /// + /// Exceptions thrown by the cuBLAS api. + /// + public class CuBLASException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The status code returned from the API + public CuBLASException(int statusCode) + : base(CuBLASException.GetErrorMessage(statusCode)) + { + this.StatusCode = statusCode; + } + + /// + /// Gets the status code returned by the cuBLAS API. + /// + public int StatusCode { get; private set; } + + /// + /// Returns the appropriate error message for each status code. + /// + /// The status code returned from the API + /// The corresponding error message + private static string GetErrorMessage(int statusCode) + { + switch (statusCode) + { + case 0: // CUBLAS_STATUS_SUCCESS + return "The operation completed successfully."; + + case 1: // CUBLAS_STATUS_NOT_INITIALIZED + return "The cuBLAS library was not initialized. This is usually caused by the lack of a prior cublasCreate() call, an error in the CUDA Runtime API called by the cuBLAS routine, or an error in the hardware setup."; + + case 2: // CUSOLVER_STATUS_ALLOC_FAILED + return "Resource allocation failed inside the cuBLAS library. This is usually caused by a cudaMalloc() failure."; + + case 7: // CUBLAS_STATUS_INVALID_VALUE + return "An unsupported value or parameter was passed to the function (a negative vector size, for example)."; + + case 8: // CUBLAS_STATUS_ARCH_MISMATCH + return "The function requires a feature absent from the device architecture; usually caused by the lack of support for double precision."; + + case 11: // CUBLAS_STATUS_MAPPING_ERROR + return "An access to GPU memory space failed, which is usually caused by a failure to bind a texture."; + + case 13: // CUBLAS_STATUS_EXECUTION_FAILED + return "The GPU program failed to execute. This is often caused by a launch failure of the kernel on the GPU, which can be caused by multiple reasons."; + + case 14: // CUBLAS_STATUS_INTERNAL_ERROR + return "An internal cuBLAS operation failed. This error is usually caused by a cudaMemcpyAsync() failure."; + + case 15: // CUBLAS_STATUS_NOT_SUPPORTED + return "The functionality requested is not supported"; + + case 16: // CUBLAS_STATUS_LICENSE_ERROR + return "The functionality requested requires some license and an error was detected when trying to check the current licensing. This error can happen if the license is not present or is expired or if the environment variable NVIDIA_LICENSE_FILE is not set properly."; + + default: + return "Unrecognized cuBLAS status code"; + } + } + } +} diff --git a/src/Numerics/Providers/LinearAlgebra/Cuda/CudaException.cs b/src/Numerics/Providers/LinearAlgebra/Cuda/CudaException.cs new file mode 100644 index 00000000..e827326a --- /dev/null +++ b/src/Numerics/Providers/LinearAlgebra/Cuda/CudaException.cs @@ -0,0 +1,90 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2013 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda +{ + /// + /// Exception thrown by the Cuda Runtime API + /// + public class CudaException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// The error code returned by the API + public CudaException(int errorCode) + : base(CudaException.GetErrorMessage(errorCode)) + { + this.ErrorCode = errorCode; + } + + /// + /// Gets the error code returned by the Cuda Runtime API. + /// + public int ErrorCode { get; private set; } + + /// + /// Gets the error message for a particular error code. + /// + /// The error code returned by the API + /// The corresponding error message + private static string GetErrorMessage(int errorCode) + { + switch (errorCode) + { + case 0: // cudaSuccess + return "The API call returned with no errors."; + + case 2: // cudaErrorMemoryAllocation + return "The API call failed because it was unable to allocate enough memory to perform the requested operation."; + + case 3: // cudaErrorInitializationError + return "The API call failed because the CUDA driver and runtime could not be initialized."; + + case 11: // cudaErrorInvalidValue + return "This indicates that one or more of the parameters passed to the API call is not within an acceptable range of values."; + + case 17: // cudaErrorInvalidDevicePointer + return "This indicates that at least one device pointer passed to the API call is not a valid device pointer. "; + + case 21: // cudaErrorInvalidMemcpyDirection + return "This indicates that the direction of the memcpy passed to the API call is not one of the types specified by cudaMemcpyKind. "; + + default: + return "Unknown Cuda Runtime error code"; + } + } + } +} diff --git a/src/Numerics/Providers/LinearAlgebra/Cuda/CudaLinearAlgebraProvider.cs b/src/Numerics/Providers/LinearAlgebra/Cuda/CudaLinearAlgebraProvider.cs index a2748d0b..e27b38d9 100644 --- a/src/Numerics/Providers/LinearAlgebra/Cuda/CudaLinearAlgebraProvider.cs +++ b/src/Numerics/Providers/LinearAlgebra/Cuda/CudaLinearAlgebraProvider.cs @@ -83,95 +83,23 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda if (a != 0 || b != -1 || linearAlgebra <=0 || _nativeRevision < 1) { - throw new NotSupportedException("Cuda Native Provider too old or not compatible. Consider upgrading to a newer version."); + throw new NotSupportedException("Cuda Native Provider not present, too old or not compatible. Consider upgrading to a newer version."); } - BLAS(SafeNativeMethods.createBLASHandle(ref _blasHandle)); - Solver(SafeNativeMethods.createSolverHandle(ref _solverHandle)); + HandleResults(SafeNativeMethods.createBLASHandle(ref _blasHandle)); + HandleResults(SafeNativeMethods.createSolverHandle(ref _solverHandle)); } - private void BLAS(int status) + private void HandleResults(CudaResults results) { - switch (status) - { - case 0: // CUBLAS_STATUS_SUCCESS - return; - - case 1: // CUBLAS_STATUS_NOT_INITIALIZED - throw new Exception("The CUDA Runtime initialization failed"); - - case 2: // CUSOLVER_STATUS_ALLOC_FAILED - throw new OutOfMemoryException("The resources could not be allocated"); - - case 7: // CUBLAS_STATUS_INVALID_VALUE - throw new ArgumentException("Invalid value"); - - case 8: // CUBLAS_STATUS_ARCH_MISMATCH - throw new NotSupportedException("The device does not support this opeation."); - - case 11: // CUBLAS_STATUS_MAPPING_ERROR - throw new Exception("Mapping error."); - - case 13: // CUBLAS_STATUS_EXECUTION_FAILED - throw new Exception("Execution failed"); + if (results.Error != 0) + throw new CudaException(results.Error); - case 14: // CUBLAS_STATUS_INTERNAL_ERROR - throw new Exception("Internal error"); + if (results.BlasStatus != 0) + throw new CuBLASException(results.BlasStatus); - case 15: // CUBLAS_STATUS_NOT_SUPPORTED - throw new NotSupportedException(); - - case 16: // CUBLAS_STATUS_LICENSE_ERROR - throw new Exception("License error"); - - default: - throw new Exception("Unrecognized cuBLAS status code: " + status); - } - } - - private void Solver(int status) - { - switch (status) - { - case 0: // CUSOLVER_STATUS_SUCCESS - return; - - case 1: // CUSOLVER_STATUS_NOT_INITIALIZED - throw new Exception("The library was not initialized"); - - case 2: // CUSOLVER_STATUS_ALLOC_FAILED - throw new OutOfMemoryException("The resources could not be allocated"); - - case 3: // CUSOLVER_STATUS_INVALID_VALUE - throw new ArgumentException("Invalid value"); - - case 4: // CUSOLVER_STATUS_ARCH_MISMATCH - throw new NotSupportedException("The device does not support compute capability 2.0 and above"); - - case 5: // CUSOLVER_STATUS_MAPPING_ERROR - throw new Exception("Mapping error"); - - case 6: // CUSOLVER_STATUS_EXECUTION_FAILED - throw new NonConvergenceException("Execution failed"); - - case 7: //CUSOLVER_STATUS_INTERNAL_ERROR - throw new Exception("Internal error"); - - case 8: // CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED - throw new ArgumentException("Matrix type not supported"); - - case 9: // CUSOLVER_STATUS_NOT_SUPPORTED - throw new NotSupportedException(); - - case 10: // CUSOLVER_STATUS_ZERO_PIVOT - throw new Exception("Zero pivot"); - - case 11: //CUSOLVER_STATUS_INVALID_LICENSE - throw new Exception("Invalid license"); - - default: - throw new Exception("Unrecognized cuSolverDn status code: " + status); - } + if (results.SolverStatus != 0) + throw new CuSolverException(results.SolverStatus); } public override string ToString() @@ -184,8 +112,8 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda public void Dispose() { - BLAS(SafeNativeMethods.destroyBLASHandle(_blasHandle)); - Solver(SafeNativeMethods.destroySolverHandle(_solverHandle)); + HandleResults(SafeNativeMethods.destroyBLASHandle(_blasHandle)); + HandleResults(SafeNativeMethods.destroySolverHandle(_solverHandle)); } } } diff --git a/src/Numerics/Providers/LinearAlgebra/Cuda/CudaResults.cs b/src/Numerics/Providers/LinearAlgebra/Cuda/CudaResults.cs new file mode 100644 index 00000000..4752de7c --- /dev/null +++ b/src/Numerics/Providers/LinearAlgebra/Cuda/CudaResults.cs @@ -0,0 +1,58 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2013 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda +{ + /// + /// Struct containing the various results from different CUDA API calls. + /// + private struct CudaResults + { + /// + /// Maps to cudaError_t + /// + public int Error; + + /// + /// Maps to cublasStatus_t + /// + public int BlasStatus; + + /// + /// Maps to cusolverStatus_t + /// + public int SolverStatus; + } +} diff --git a/src/Numerics/Providers/LinearAlgebra/Cuda/SafeNativeMethods.cs b/src/Numerics/Providers/LinearAlgebra/Cuda/SafeNativeMethods.cs index ba58a483..66937ac9 100644 --- a/src/Numerics/Providers/LinearAlgebra/Cuda/SafeNativeMethods.cs +++ b/src/Numerics/Providers/LinearAlgebra/Cuda/SafeNativeMethods.cs @@ -54,66 +54,66 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Cuda internal static extern int query_capability(int capability); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int createBLASHandle(ref IntPtr blasHandle); + internal static extern CudaResults createBLASHandle(ref IntPtr blasHandle); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int destroyBLASHandle(IntPtr blasHandle); + internal static extern CudaResults destroyBLASHandle(IntPtr blasHandle); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int createSolverHandle(ref IntPtr solverHandle); + internal static extern CudaResults createSolverHandle(ref IntPtr solverHandle); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern int destroySolverHandle(IntPtr solverHandle); + internal static extern CudaResults destroySolverHandle(IntPtr solverHandle); #region BLAS [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void s_axpy(IntPtr blasHandle, int n, float alpha, float[] x, [In, Out] float[] y); + internal static extern CudaResults s_axpy(IntPtr blasHandle, int n, float alpha, float[] x, [In, Out] float[] y); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void d_axpy(IntPtr blasHandle, int n, double alpha, double[] x, [In, Out] double[] y); + internal static extern CudaResults d_axpy(IntPtr blasHandle, int n, double alpha, double[] x, [In, Out] double[] y); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void c_axpy(IntPtr blasHandle, int n, Complex32 alpha, Complex32[] x, [In, Out] Complex32[] y); + internal static extern CudaResults c_axpy(IntPtr blasHandle, int n, Complex32 alpha, Complex32[] x, [In, Out] Complex32[] y); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void z_axpy(IntPtr blasHandle, int n, Complex alpha, Complex[] x, [In, Out] Complex[] y); + internal static extern CudaResults z_axpy(IntPtr blasHandle, int n, Complex alpha, Complex[] x, [In, Out] Complex[] y); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void s_scale(IntPtr blasHandle, int n, float alpha, [Out] float[] x); + internal static extern CudaResults s_scale(IntPtr blasHandle, int n, float alpha, [Out] float[] x); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void d_scale(IntPtr blasHandle, int n, double alpha, [Out] double[] x); + internal static extern CudaResults d_scale(IntPtr blasHandle, int n, double alpha, [Out] double[] x); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void c_scale(IntPtr blasHandle, int n, Complex32 alpha, [In, Out] Complex32[] x); + internal static extern CudaResults c_scale(IntPtr blasHandle, int n, Complex32 alpha, [In, Out] Complex32[] x); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void z_scale(IntPtr blasHandle, int n, Complex alpha, [In, Out] Complex[] x); + internal static extern CudaResults z_scale(IntPtr blasHandle, int n, Complex alpha, [In, Out] Complex[] x); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern float s_dot_product(IntPtr blasHandle, int n, float[] x, float[] y); + internal static extern CudaResults s_dot_product(IntPtr blasHandle, int n, float[] x, float[] y, ref float result); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern double d_dot_product(IntPtr blasHandle, int n, double[] x, double[] y); + internal static extern CudaResults d_dot_product(IntPtr blasHandle, int n, double[] x, double[] y, ref double result); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern Complex32 c_dot_product(IntPtr blasHandle, int n, Complex32[] x, Complex32[] y); + internal static extern CudaResults c_dot_product(IntPtr blasHandle, int n, Complex32[] x, Complex32[] y, ref Complex32 result); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern Complex z_dot_product(IntPtr blasHandle, int n, Complex[] x, Complex[] y); + internal static extern CudaResults z_dot_product(IntPtr blasHandle, int n, Complex[] x, Complex[] y, ref Complex result); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void s_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, float alpha, float[] x, float[] y, float beta, [In, Out] float[] c); + internal static extern CudaResults s_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, float alpha, float[] x, float[] y, float beta, [In, Out] float[] c); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void d_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, double alpha, double[] x, double[] y, double beta, [In, Out] double[] c); + internal static extern CudaResults d_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, double alpha, double[] x, double[] y, double beta, [In, Out] double[] c); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void c_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, Complex32 alpha, Complex32[] x, Complex32[] y, Complex32 beta, [In, Out] Complex32[] c); + internal static extern CudaResults c_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, Complex32 alpha, Complex32[] x, Complex32[] y, Complex32 beta, [In, Out] Complex32[] c); [DllImport(_DllName, ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - internal static extern void z_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, Complex alpha, Complex[] x, Complex[] y, Complex beta, [In, Out] Complex[] c); + internal static extern CudaResults z_matrix_multiply(IntPtr blasHandle, int transA, int transB, int m, int n, int k, Complex alpha, Complex[] x, Complex[] y, Complex beta, [In, Out] Complex[] c); internal static int ToCUDA(this Transpose transpose) {