suanPan
Loading...
Searching...
No Matches
FullMatCUDA.hpp
Go to the documentation of this file.
1/*******************************************************************************
2 * Copyright (C) 2017-2025 Theodore Chang
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
29// ReSharper disable CppCStyleCast
30#ifndef FULLMATCUDA_HPP
31#define FULLMATCUDA_HPP
32
33#include <cuda_runtime.h>
34#include <cusolverDn.h>
35#include "FullMat.hpp"
36
37template<sp_d T> class FullMatCUDA final : public FullMat<T> {
38 cusolverDnHandle_t handle = nullptr;
39 cudaStream_t stream = nullptr;
40
41 int* info = nullptr;
42 int* ipiv = nullptr;
43 void* d_A = nullptr;
44 void* buffer = nullptr;
45
46 void acquire() {
47 cusolverDnCreate(&handle);
48 cudaStreamCreate(&stream);
49 cusolverDnSetStream(handle, stream);
50
51 cudaMalloc(&info, sizeof(int));
52 cudaMemset(info, 0, sizeof(int));
53 cudaMalloc(&ipiv, sizeof(int) * this->n_rows);
54
55 int bufferSize = 0;
56 if constexpr(std::is_same_v<T, float>) {
57 cudaMalloc(&d_A, sizeof(float) * this->n_elem);
58 cusolverDnSgetrf_bufferSize(handle, static_cast<int>(this->n_rows), static_cast<int>(this->n_cols), (float*)d_A, static_cast<int>(this->n_elem), &bufferSize);
59 cudaMalloc(&buffer, sizeof(float) * bufferSize);
60 }
61 else if(Precision::MIXED == this->setting.precision) {
62 cudaMalloc(&d_A, sizeof(float) * this->n_elem);
63 cusolverDnSgetrf_bufferSize(handle, static_cast<int>(this->n_rows), static_cast<int>(this->n_cols), (float*)d_A, static_cast<int>(this->n_elem), &bufferSize);
64 cudaMalloc(&buffer, sizeof(float) * bufferSize);
65 }
66 else {
67 cudaMalloc(&d_A, sizeof(double) * this->n_elem);
68 cusolverDnDgetrf_bufferSize(handle, static_cast<int>(this->n_rows), static_cast<int>(this->n_cols), (double*)d_A, static_cast<int>(this->n_elem), &bufferSize);
69 cudaMalloc(&buffer, sizeof(double) * bufferSize);
70 }
71 }
72
73 void release() const {
74 if(handle) cusolverDnDestroy(handle);
75 if(stream) cudaStreamDestroy(stream);
76
77 if(info) cudaFree(info);
78 if(d_A) cudaFree(d_A);
79 if(buffer) cudaFree(buffer);
80 if(ipiv) cudaFree(ipiv);
81 }
82
83protected:
84 int direct_solve(Mat<T>& X, Mat<T>&& B) override { return this->direct_solve(X, B); }
85
86 int direct_solve(Mat<T>&, const Mat<T>&) override;
87
88public:
89 FullMatCUDA(const uword in_rows, const uword in_cols)
90 : FullMat<T>(in_rows, in_cols) { acquire(); }
91
93 : FullMat<T>(other) { acquire(); }
94
95 FullMatCUDA(FullMatCUDA&&) noexcept = delete;
96 FullMatCUDA& operator=(const FullMatCUDA&) = delete;
97 FullMatCUDA& operator=(FullMatCUDA&&) noexcept = delete;
98
99 ~FullMatCUDA() override { release(); }
100
101 unique_ptr<MetaMat<T>> make_copy() override { return std::make_unique<FullMatCUDA>(*this); }
102};
103
104template<sp_d T> int FullMatCUDA<T>::direct_solve(Mat<T>& X, const Mat<T>& B) {
105 if constexpr(std::is_same_v<T, float>) {
106 // pure float
107 if(!this->factored) {
108 this->factored = true;
109 cudaMemcpyAsync(d_A, this->memptr(), sizeof(float) * this->n_elem, cudaMemcpyHostToDevice, stream);
110 cusolverDnSgetrf(handle, static_cast<int>(this->n_rows), static_cast<int>(this->n_cols), (float*)d_A, static_cast<int>(this->n_rows), (float*)buffer, ipiv, info);
111 }
112
113 const size_t byte_size = sizeof(float) * B.n_elem;
114
115 void* d_x = nullptr;
116 cudaMalloc(&d_x, byte_size);
117 cudaMemcpyAsync(d_x, B.memptr(), byte_size, cudaMemcpyHostToDevice, stream);
118 cusolverDnSgetrs(handle, CUBLAS_OP_N, static_cast<int>(this->n_rows), static_cast<int>(B.n_cols), (float*)d_A, static_cast<int>(this->n_rows), ipiv, (float*)d_x, static_cast<int>(this->n_rows), info);
119
120 X.set_size(arma::size(B));
121
122 cudaMemcpyAsync(X.memptr(), d_x, byte_size, cudaMemcpyDeviceToHost, stream);
123
124 cudaDeviceSynchronize();
125
126 if(d_x) cudaFree(d_x);
127 }
128 else if(Precision::MIXED == this->setting.precision) {
129 // mixed precision
130 if(!this->factored) {
131 this->factored = true;
132 this->s_memory = this->to_float();
133 cudaMemcpyAsync(d_A, this->s_memory.memptr(), sizeof(float) * this->s_memory.n_elem, cudaMemcpyHostToDevice, stream);
134 cusolverDnSgetrf(handle, static_cast<int>(this->n_rows), static_cast<int>(this->n_cols), (float*)d_A, static_cast<int>(this->n_rows), (float*)buffer, ipiv, info);
135 }
136
137 const size_t byte_size = sizeof(float) * B.n_elem;
138
139 void* d_x = nullptr;
140 cudaMalloc(&d_x, byte_size);
141
142 X = arma::zeros(B.n_rows, B.n_cols);
143
144 mat full_residual = B;
145
146 auto multiplier = norm(full_residual);
147
148 auto counter = std::uint8_t{0};
149 while(counter++ < this->setting.iterative_refinement) {
150 if(multiplier < this->setting.tolerance) break;
151
152 auto residual = conv_to<fmat>::from(full_residual / multiplier);
153
154 cudaMemcpyAsync(d_x, residual.memptr(), byte_size, cudaMemcpyHostToDevice, stream);
155 cusolverDnSgetrs(handle, CUBLAS_OP_N, static_cast<int>(this->n_rows), static_cast<int>(B.n_cols), (float*)d_A, static_cast<int>(this->n_rows), ipiv, (float*)d_x, static_cast<int>(this->n_rows), info);
156 cudaMemcpyAsync(residual.memptr(), d_x, byte_size, cudaMemcpyDeviceToHost, stream);
157
158 cudaDeviceSynchronize();
159
160 const mat incre = multiplier * conv_to<mat>::from(residual);
161
162 X += incre;
163
164 suanpan_debug("Mixed precision algorithm multiplier: {:.5E}.\n", multiplier = arma::norm(full_residual -= this->operator*(incre)));
165 }
166
167 if(d_x) cudaFree(d_x);
168 }
169 else {
170 // pure double
171 if(!this->factored) {
172 this->factored = true;
173 cudaMemcpyAsync(d_A, this->memptr(), sizeof(double) * this->n_elem, cudaMemcpyHostToDevice, stream);
174 cusolverDnDgetrf(handle, static_cast<int>(this->n_rows), static_cast<int>(this->n_cols), (double*)d_A, static_cast<int>(this->n_rows), (double*)buffer, ipiv, info);
175 }
176
177 const size_t byte_size = sizeof(double) * B.n_elem;
178
179 void* d_x = nullptr;
180 cudaMalloc(&d_x, byte_size);
181 cudaMemcpyAsync(d_x, B.memptr(), byte_size, cudaMemcpyHostToDevice, stream);
182 cusolverDnDgetrs(handle, CUBLAS_OP_N, static_cast<int>(this->n_rows), static_cast<int>(B.n_cols), (double*)d_A, static_cast<int>(this->n_rows), ipiv, (double*)d_x, static_cast<int>(this->n_rows), info);
183
184 X.set_size(arma::size(B));
185
186 cudaMemcpyAsync(X.memptr(), d_x, byte_size, cudaMemcpyDeviceToHost, stream);
187
188 cudaDeviceSynchronize();
189
190 if(d_x) cudaFree(d_x);
191 }
192
193 return 0;
194}
195
196#endif
197
A FullMatCUDA class that holds matrices.
Definition FullMatCUDA.hpp:37
FullMatCUDA(const uword in_rows, const uword in_cols)
Definition FullMatCUDA.hpp:89
FullMatCUDA(FullMatCUDA &&) noexcept=delete
int direct_solve(Mat< T > &X, Mat< T > &&B) override
Definition FullMatCUDA.hpp:84
FullMatCUDA(const FullMatCUDA &other)
Definition FullMatCUDA.hpp:92
unique_ptr< MetaMat< T > > make_copy() override
Definition FullMatCUDA.hpp:101
A FullMat class that holds matrices.
Definition FullMat.hpp:35
const uword n_cols
Definition MetaMat.hpp:117
const uword n_rows
Definition MetaMat.hpp:116
const uword n_elem
Definition MetaMat.hpp:118
SolverSetting< T > setting
Definition MetaMat.hpp:78
Precision precision
Definition SolverSetting.hpp:32
#define suanpan_debug(...)
Definition suanPan.h:371