75d87fa8ceae5f6b9f46a0d56d893ab196b6f4b8
[alexxy/gromacs.git] / src / gromacs / gpu_utils / cudautils.cu
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2014,2015,2016,2017, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 #include "gmxpre.h"
37
38 #include "cudautils.cuh"
39
40 #include <cassert>
41 #include <cstdlib>
42
43 #include "gromacs/gpu_utils/cuda_arch_utils.cuh"
44 #include "gromacs/utility/smalloc.h"
45
46 /*** Generic CUDA data operation wrappers ***/
47
48 /*! Launches synchronous or asynchronous host to device memory copy.
49  *
50  *  The copy is launched in stream s or if not specified, in stream 0.
51  */
52 static int cu_copy_D2H_generic(void * h_dest, void * d_src, size_t bytes,
53                                bool bAsync = false, cudaStream_t s = 0)
54 {
55     cudaError_t stat;
56
57     if (h_dest == NULL || d_src == NULL || bytes == 0)
58     {
59         return -1;
60     }
61
62     if (bAsync)
63     {
64         stat = cudaMemcpyAsync(h_dest, d_src, bytes, cudaMemcpyDeviceToHost, s);
65         CU_RET_ERR(stat, "DtoH cudaMemcpyAsync failed");
66
67     }
68     else
69     {
70         stat = cudaMemcpy(h_dest, d_src, bytes, cudaMemcpyDeviceToHost);
71         CU_RET_ERR(stat, "DtoH cudaMemcpy failed");
72     }
73
74     return 0;
75 }
76
77 int cu_copy_D2H(void * h_dest, void * d_src, size_t bytes)
78 {
79     return cu_copy_D2H_generic(h_dest, d_src, bytes, false);
80 }
81
82 /*!
83  *  The copy is launched in stream s or if not specified, in stream 0.
84  */
85 int cu_copy_D2H_async(void * h_dest, void * d_src, size_t bytes, cudaStream_t s = 0)
86 {
87     return cu_copy_D2H_generic(h_dest, d_src, bytes, true, s);
88 }
89
90 /*! Launches synchronous or asynchronous device to host memory copy.
91  *
92  *  The copy is launched in stream s or if not specified, in stream 0.
93  */
94 static int cu_copy_H2D_generic(void * d_dest, void * h_src, size_t bytes,
95                                bool bAsync = false, cudaStream_t s = 0)
96 {
97     cudaError_t stat;
98
99     if (d_dest == NULL || h_src == NULL || bytes == 0)
100     {
101         return -1;
102     }
103
104     if (bAsync)
105     {
106         stat = cudaMemcpyAsync(d_dest, h_src, bytes, cudaMemcpyHostToDevice, s);
107         CU_RET_ERR(stat, "HtoD cudaMemcpyAsync failed");
108     }
109     else
110     {
111         stat = cudaMemcpy(d_dest, h_src, bytes, cudaMemcpyHostToDevice);
112         CU_RET_ERR(stat, "HtoD cudaMemcpy failed");
113     }
114
115     return 0;
116 }
117
118 int cu_copy_H2D(void * d_dest, void * h_src, size_t bytes)
119 {
120     return cu_copy_H2D_generic(d_dest, h_src, bytes, false);
121 }
122
123 /*!
124  *  The copy is launched in stream s or if not specified, in stream 0.
125  */
126 int cu_copy_H2D_async(void * d_dest, void * h_src, size_t bytes, cudaStream_t s = 0)
127 {
128     return cu_copy_H2D_generic(d_dest, h_src, bytes, true, s);
129 }
130
131 float cu_event_elapsed(cudaEvent_t start, cudaEvent_t end)
132 {
133     float       t = 0.0;
134     cudaError_t stat;
135
136     stat = cudaEventElapsedTime(&t, start, end);
137     CU_RET_ERR(stat, "cudaEventElapsedTime failed in cu_event_elapsed");
138
139     return t;
140 }
141
142 int cu_wait_event(cudaEvent_t e)
143 {
144     cudaError_t s;
145
146     s = cudaEventSynchronize(e);
147     CU_RET_ERR(s, "cudaEventSynchronize failed in cu_wait_event");
148
149     return 0;
150 }
151
152 /*!
153  *  If time != NULL it also calculates the time elapsed between start and end and
154  *  return this is milliseconds.
155  */
156 int cu_wait_event_time(cudaEvent_t end, cudaEvent_t start, float *time)
157 {
158     cudaError_t s;
159
160     s = cudaEventSynchronize(end);
161     CU_RET_ERR(s, "cudaEventSynchronize failed in cu_wait_event");
162
163     if (time)
164     {
165         *time = cu_event_elapsed(start, end);
166     }
167
168     return 0;
169 }
170
171 /**** Operation on buffered arrays (arrays with "over-allocation" in gmx wording) *****/
172
173 /*!
174  * If the pointers to the size variables are NULL no resetting happens.
175  */
176 void cu_free_buffered(void *d_ptr, int *n, int *nalloc)
177 {
178     cudaError_t stat;
179
180     if (d_ptr)
181     {
182         stat = cudaFree(d_ptr);
183         CU_RET_ERR(stat, "cudaFree failed");
184     }
185
186     if (n)
187     {
188         *n = -1;
189     }
190
191     if (nalloc)
192     {
193         *nalloc = -1;
194     }
195 }
196
197 /*!
198  *  Reallocation of the memory pointed by d_ptr and copying of the data from
199  *  the location pointed by h_src host-side pointer is done. Allocation is
200  *  buffered and therefore freeing is only needed if the previously allocated
201  *  space is not enough.
202  *  The H2D copy is launched in stream s and can be done synchronously or
203  *  asynchronously (the default is the latter).
204  */
205 void cu_realloc_buffered(void **d_dest, void *h_src,
206                          size_t type_size,
207                          int *curr_size, int *curr_alloc_size,
208                          int req_size,
209                          cudaStream_t s,
210                          bool bAsync = true)
211 {
212     cudaError_t stat;
213
214     if (d_dest == NULL || req_size < 0)
215     {
216         return;
217     }
218
219     /* reallocate only if the data does not fit = allocation size is smaller
220        than the current requested size */
221     if (req_size > *curr_alloc_size)
222     {
223         /* only free if the array has already been initialized */
224         if (*curr_alloc_size >= 0)
225         {
226             cu_free_buffered(*d_dest, curr_size, curr_alloc_size);
227         }
228
229         *curr_alloc_size = over_alloc_large(req_size);
230
231         stat = cudaMalloc(d_dest, *curr_alloc_size * type_size);
232         CU_RET_ERR(stat, "cudaMalloc failed in cu_free_buffered");
233     }
234
235     /* size could have changed without actual reallocation */
236     *curr_size = req_size;
237
238     /* upload to device */
239     if (h_src)
240     {
241         if (bAsync)
242         {
243             cu_copy_H2D_async(*d_dest, h_src, *curr_size * type_size, s);
244         }
245         else
246         {
247             cu_copy_H2D(*d_dest, h_src,  *curr_size * type_size);
248         }
249     }
250 }
251
252 bool use_texobj(const gmx_device_info_t *dev_info)
253 {
254     assert(!c_disableCudaTextures);
255     /* Only device CC >= 3.0 (Kepler and later) support texture objects */
256     return (dev_info->prop.major >= 3);
257 }
258
259 /*! \brief Set up texture object for an array of type T.
260  *
261  * Set up texture object for an array of type T and bind it to the device memory
262  * \p d_ptr points to.
263  *
264  * \tparam[in] T        Raw data type
265  * \param[out] texObj   texture object to initialize
266  * \param[in]  d_ptr    pointer to device global memory to bind \p texObj to
267  * \param[in]  sizeInBytes  size of memory area to bind \p texObj to
268  */
269 template <typename T>
270 static void setup1DTexture(cudaTextureObject_t &texObj,
271                            void                *d_ptr,
272                            size_t               sizeInBytes)
273 {
274     assert(!c_disableCudaTextures);
275
276     cudaError_t      stat;
277     cudaResourceDesc rd;
278     cudaTextureDesc  td;
279
280     memset(&rd, 0, sizeof(rd));
281     rd.resType                = cudaResourceTypeLinear;
282     rd.res.linear.devPtr      = d_ptr;
283     rd.res.linear.desc        = cudaCreateChannelDesc<T>();
284     rd.res.linear.sizeInBytes = sizeInBytes;
285
286     memset(&td, 0, sizeof(td));
287     td.readMode                 = cudaReadModeElementType;
288     stat = cudaCreateTextureObject(&texObj, &rd, &td, NULL);
289     CU_RET_ERR(stat, "cudaCreateTextureObject failed");
290 }
291
292 /*! \brief Set up texture reference for an array of type T.
293  *
294  * Set up texture object for an array of type T and bind it to the device memory
295  * \p d_ptr points to.
296  *
297  * \tparam[in] T        Raw data type
298  * \param[out] texObj   texture reference to initialize
299  * \param[in]  d_ptr    pointer to device global memory to bind \p texObj to
300  * \param[in]  sizeInBytes  size of memory area to bind \p texObj to
301  */
302 template <typename T>
303 static void setup1DTexture(const struct texture<T, 1, cudaReadModeElementType> *texRef,
304                            const void                                          *d_ptr,
305                            size_t                                              sizeInBytes)
306 {
307     assert(!c_disableCudaTextures);
308
309     cudaError_t           stat;
310     cudaChannelFormatDesc cd;
311
312     cd   = cudaCreateChannelDesc<T>();
313     stat = cudaBindTexture(nullptr, texRef, d_ptr, &cd, sizeInBytes);
314     CU_RET_ERR(stat, "cudaBindTexture failed");
315 }
316
317 template <typename T>
318 void initParamLookupTable(T                        * &d_ptr,
319                           cudaTextureObject_t       &texObj,
320                           const struct texture<T, 1, cudaReadModeElementType> *texRef,
321                           const T                   *h_ptr,
322                           int                        numElem,
323                           const gmx_device_info_t   *devInfo)
324 {
325     const size_t sizeInBytes = numElem * sizeof(*d_ptr);
326     cudaError_t  stat        = cudaMalloc((void **)&d_ptr, sizeInBytes);
327     CU_RET_ERR(stat, "cudaMalloc failed in initParamLookupTable");
328     cu_copy_H2D(d_ptr, (void *)h_ptr, sizeInBytes);
329
330     if (!c_disableCudaTextures)
331     {
332         if (use_texobj(devInfo))
333         {
334             setup1DTexture<T>(texObj, d_ptr, sizeInBytes);
335         }
336         else
337         {
338             setup1DTexture<T>(texRef, d_ptr, sizeInBytes);
339         }
340     }
341 }
342
343 //! Add explicit instantiations of initParamLookupTable() here as needed
344 template void initParamLookupTable<float>(float * &, cudaTextureObject_t &, const texture<float, 1, cudaReadModeElementType> *, const float *, int, const gmx_device_info_t *);