Implement OpenCL support
[alexxy/gromacs.git] / src / gromacs / gmxlib / gpu_utils / gpu_utils_ocl.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015, 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 /*! \internal \file
36  *  \brief Define functions for detection and initialization for OpenCL devices.
37  *
38  *  \author Anca Hamuraru <anca@streamcomputing.eu>
39  *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
40  *  \author Teemu Virolainen <teemu@streamcomputing.eu>
41  */
42
43 #include "gmxpre.h"
44
45 #include <assert.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include <memory.h>
51
52 #include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
53 #include "gromacs/gmxlib/gpu_utils/ocl_compiler.h"
54 #include "gromacs/gmxlib/ocl_tools/oclutils.h"
55 #include "gromacs/legacyheaders/types/enums.h"
56 #include "gromacs/legacyheaders/types/hw_info.h"
57 #include "gromacs/utility/cstringutil.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/smalloc.h"
60
61 /*! \brief Helper macro for error handling */
62 #define CALLOCLFUNC_LOGERROR(func, err_str, retval) { \
63         cl_int opencl_ret = func; \
64         if (CL_SUCCESS != opencl_ret) \
65         { \
66             sprintf(err_str, "OpenCL error %d", opencl_ret); \
67             retval = -1; \
68         } \
69         else{ \
70             retval = 0; } \
71 }
72
73
74 /*! \brief Helper function that checks whether a given GPU status indicates compatible GPU.
75  *
76  * \param[in] stat  GPU status.
77  * \returns         true if the provided status is egpuCompatible, otherwise false.
78  */
79 static bool is_compatible_gpu(int stat)
80 {
81     return (stat == egpuCompatible);
82 }
83
84 /*! \brief Returns true if the gpu characterized by the device properties is
85  *  supported by the native gpu acceleration.
86  * \returns             true if the GPU properties passed indicate a compatible
87  *                      GPU, otherwise false.
88  */
89 static int is_gmx_supported_gpu_id(struct gmx_device_info_t *ocl_gpu_device)
90 {
91     /* Only AMD and NVIDIA GPUs are supported for now */
92     if ((OCL_VENDOR_NVIDIA == ocl_gpu_device->vendor_e) ||
93         (OCL_VENDOR_AMD == ocl_gpu_device->vendor_e))
94     {
95         return egpuCompatible;
96     }
97
98     return egpuIncompatible;
99 }
100
101 /*! \brief Returns an ocl_vendor_id_t value corresponding to the input OpenCL vendor name.
102  *
103  *  \param[in] vendor_name String with OpenCL vendor name.
104  *  \returns               ocl_vendor_id_t value for the input vendor_name
105  */
106 ocl_vendor_id_t get_vendor_id(char *vendor_name)
107 {
108     if (vendor_name)
109     {
110         if (strstr(vendor_name, "NVIDIA"))
111         {
112             return OCL_VENDOR_NVIDIA;
113         }
114         else
115         if (strstr(vendor_name, "AMD") ||
116             strstr(vendor_name, "Advanced Micro Devices"))
117         {
118             return OCL_VENDOR_AMD;
119         }
120         else
121         if (strstr(vendor_name, "Intel"))
122         {
123             return OCL_VENDOR_INTEL;
124         }
125     }
126     return OCL_VENDOR_UNKNOWN;
127 }
128
129
130 //! This function is documented in the header file
131 int detect_gpus(gmx_gpu_info_t *gpu_info, char *err_str)
132 {
133     int             retval;
134     cl_uint         ocl_platform_count;
135     cl_platform_id *ocl_platform_ids;
136     cl_device_type  req_dev_type = CL_DEVICE_TYPE_GPU;
137
138     retval           = 0;
139     ocl_platform_ids = NULL;
140
141     if (getenv("GMX_OCL_FORCE_CPU") != NULL)
142     {
143         req_dev_type = CL_DEVICE_TYPE_CPU;
144     }
145
146     while (1)
147     {
148         CALLOCLFUNC_LOGERROR(clGetPlatformIDs(0, NULL, &ocl_platform_count), err_str, retval)
149         if (0 != retval)
150         {
151             break;
152         }
153
154         if (1 > ocl_platform_count)
155         {
156             break;
157         }
158
159         snew(ocl_platform_ids, ocl_platform_count);
160
161         CALLOCLFUNC_LOGERROR(clGetPlatformIDs(ocl_platform_count, ocl_platform_ids, NULL), err_str, retval)
162         if (0 != retval)
163         {
164             break;
165         }
166
167         for (unsigned int i = 0; i < ocl_platform_count; i++)
168         {
169             cl_uint ocl_device_count;
170
171             /* If requesting req_dev_type devices fails, just go to the next platform */
172             if (CL_SUCCESS != clGetDeviceIDs(ocl_platform_ids[i], req_dev_type, 0, NULL, &ocl_device_count))
173             {
174                 continue;
175             }
176
177             if (1 <= ocl_device_count)
178             {
179                 gpu_info->n_dev += ocl_device_count;
180             }
181         }
182
183         if (1 > gpu_info->n_dev)
184         {
185             break;
186         }
187
188         snew(gpu_info->gpu_dev, gpu_info->n_dev);
189
190         {
191             int           device_index;
192             cl_device_id *ocl_device_ids;
193
194             snew(ocl_device_ids, gpu_info->n_dev);
195             device_index = 0;
196
197             for (unsigned int i = 0; i < ocl_platform_count; i++)
198             {
199                 cl_uint ocl_device_count;
200
201                 /* If requesting req_dev_type devices fails, just go to the next platform */
202                 if (CL_SUCCESS != clGetDeviceIDs(ocl_platform_ids[i], req_dev_type, gpu_info->n_dev, ocl_device_ids, &ocl_device_count))
203                 {
204                     continue;
205                 }
206
207                 if (1 > ocl_device_count)
208                 {
209                     break;
210                 }
211
212                 for (unsigned int j = 0; j < ocl_device_count; j++)
213                 {
214                     gpu_info->gpu_dev[device_index].ocl_gpu_id.ocl_platform_id = ocl_platform_ids[i];
215                     gpu_info->gpu_dev[device_index].ocl_gpu_id.ocl_device_id   = ocl_device_ids[j];
216
217                     gpu_info->gpu_dev[device_index].device_name[0] = 0;
218                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_NAME, sizeof(gpu_info->gpu_dev[device_index].device_name), gpu_info->gpu_dev[device_index].device_name, NULL);
219
220                     gpu_info->gpu_dev[device_index].device_version[0] = 0;
221                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_VERSION, sizeof(gpu_info->gpu_dev[device_index].device_version), gpu_info->gpu_dev[device_index].device_version, NULL);
222
223                     gpu_info->gpu_dev[device_index].device_vendor[0] = 0;
224                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_VENDOR, sizeof(gpu_info->gpu_dev[device_index].device_vendor), gpu_info->gpu_dev[device_index].device_vendor, NULL);
225
226                     gpu_info->gpu_dev[device_index].compute_units = 0;
227                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(gpu_info->gpu_dev[device_index].compute_units), &(gpu_info->gpu_dev[device_index].compute_units), NULL);
228
229                     gpu_info->gpu_dev[device_index].adress_bits = 0;
230                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_ADDRESS_BITS, sizeof(gpu_info->gpu_dev[device_index].adress_bits), &(gpu_info->gpu_dev[device_index].adress_bits), NULL);
231
232                     gpu_info->gpu_dev[device_index].vendor_e = get_vendor_id(gpu_info->gpu_dev[device_index].device_vendor);
233
234                     gpu_info->gpu_dev[device_index].stat = is_gmx_supported_gpu_id(gpu_info->gpu_dev + device_index);
235
236                     if (egpuCompatible == gpu_info->gpu_dev[device_index].stat)
237                     {
238                         gpu_info->n_dev_compatible++;
239                     }
240
241                     device_index++;
242                 }
243             }
244
245             gpu_info->n_dev = device_index;
246
247             /* Dummy sort of devices -  AMD first, then NVIDIA, then Intel */
248             // TODO: Sort devices based on performance.
249             if (0 < gpu_info->n_dev)
250             {
251                 int last = -1;
252                 for (int i = 0; i < gpu_info->n_dev; i++)
253                 {
254                     if (OCL_VENDOR_AMD == gpu_info->gpu_dev[i].vendor_e)
255                     {
256                         last++;
257
258                         if (last < i)
259                         {
260                             gmx_device_info_t ocl_gpu_info;
261                             ocl_gpu_info            = gpu_info->gpu_dev[i];
262                             gpu_info->gpu_dev[i]    = gpu_info->gpu_dev[last];
263                             gpu_info->gpu_dev[last] = ocl_gpu_info;
264                         }
265                     }
266                 }
267
268                 /* if more than 1 device left to be sorted */
269                 if ((gpu_info->n_dev - 1 - last) > 1)
270                 {
271                     for (int i = 0; i < gpu_info->n_dev; i++)
272                     {
273                         if (OCL_VENDOR_NVIDIA == gpu_info->gpu_dev[i].vendor_e)
274                         {
275                             last++;
276
277                             if (last < i)
278                             {
279                                 gmx_device_info_t ocl_gpu_info;
280                                 ocl_gpu_info            = gpu_info->gpu_dev[i];
281                                 gpu_info->gpu_dev[i]    = gpu_info->gpu_dev[last];
282                                 gpu_info->gpu_dev[last] = ocl_gpu_info;
283                             }
284                         }
285                     }
286                 }
287             }
288
289             sfree(ocl_device_ids);
290         }
291
292         break;
293     }
294
295     sfree(ocl_platform_ids);
296
297     return retval;
298 }
299
300 //! This function is documented in the header file
301 void free_gpu_info(const gmx_gpu_info_t gmx_unused *gpu_info)
302 {
303     if (gpu_info)
304     {
305         for (int i = 0; i < gpu_info->n_dev; i++)
306         {
307             cl_int gmx_unused cl_error;
308
309             if (gpu_info->gpu_dev[i].context)
310             {
311                 cl_error                     = clReleaseContext(gpu_info->gpu_dev[i].context);
312                 gpu_info->gpu_dev[i].context = NULL;
313                 assert(CL_SUCCESS == cl_error);
314             }
315
316             if (gpu_info->gpu_dev[i].program)
317             {
318                 cl_error                     = clReleaseProgram(gpu_info->gpu_dev[i].program);
319                 gpu_info->gpu_dev[i].program = NULL;
320                 assert(CL_SUCCESS == cl_error);
321             }
322         }
323
324         sfree(gpu_info->gpu_dev);
325     }
326 }
327
328 //! This function is documented in the header file
329 void pick_compatible_gpus(const gmx_gpu_info_t *gpu_info,
330                           gmx_gpu_opt_t        *gpu_opt)
331 {
332     int  i, ncompat;
333     int *compat;
334
335     assert(gpu_info);
336     /* gpu_dev/n_dev have to be either NULL/0 or not (NULL/0) */
337     assert((gpu_info->n_dev != 0 ? 0 : 1) ^ (gpu_info->gpu_dev == NULL ? 0 : 1));
338
339     snew(compat, gpu_info->n_dev);
340     ncompat = 0;
341     for (i = 0; i < gpu_info->n_dev; i++)
342     {
343         if (is_compatible_gpu(gpu_info->gpu_dev[i].stat))
344         {
345             ncompat++;
346             compat[ncompat - 1] = i;
347         }
348     }
349
350     gpu_opt->n_dev_compatible = ncompat;
351     snew(gpu_opt->dev_compatible, ncompat);
352     memcpy(gpu_opt->dev_compatible, compat, ncompat*sizeof(*compat));
353     sfree(compat);
354 }
355
356 //! This function is documented in the header file
357 gmx_bool check_selected_gpus(int                  *checkres,
358                              const gmx_gpu_info_t *gpu_info,
359                              gmx_gpu_opt_t        *gpu_opt)
360 {
361     int  i, id;
362     bool bAllOk;
363
364     assert(checkres);
365     assert(gpu_info);
366     assert(gpu_opt->n_dev_use >= 0);
367
368     if (gpu_opt->n_dev_use == 0)
369     {
370         return TRUE;
371     }
372
373     assert(gpu_opt->dev_use);
374
375     /* we will assume that all GPUs requested are valid IDs,
376        otherwise we'll bail anyways */
377
378     bAllOk = true;
379     for (i = 0; i < gpu_opt->n_dev_use; i++)
380     {
381         id = gpu_opt->dev_use[i];
382
383         /* devices are stored in increasing order of IDs in gpu_dev */
384         gpu_opt->dev_use[i] = id;
385
386         checkres[i] = (id >= gpu_info->n_dev) ?
387             egpuNonexistent : gpu_info->gpu_dev[id].stat;
388
389         bAllOk = bAllOk && is_compatible_gpu(checkres[i]);
390     }
391
392     return bAllOk;
393 }
394
395 //! This function is documented in the header file
396 void get_gpu_device_info_string(char gmx_unused *s, const gmx_gpu_info_t gmx_unused *gpu_info, int gmx_unused index)
397 {
398     assert(s);
399     assert(gpu_info);
400
401     if (index < 0 && index >= gpu_info->n_dev)
402     {
403         return;
404     }
405
406     gmx_device_info_t  *dinfo = &gpu_info->gpu_dev[index];
407
408     bool                bGpuExists =
409         dinfo->stat == egpuCompatible ||
410         dinfo->stat == egpuIncompatible;
411
412     if (!bGpuExists)
413     {
414         sprintf(s, "#%d: %s, stat: %s",
415                 index, "N/A",
416                 gpu_detect_res_str[dinfo->stat]);
417     }
418     else
419     {
420         sprintf(s, "#%d: name: %s, vendor: %s, device version: %s, stat: %s",
421                 index, dinfo->device_name, dinfo->device_vendor,
422                 dinfo->device_version,
423                 gpu_detect_res_str[dinfo->stat]);
424     }
425 }
426
427 //! This function is documented in the header file
428 gmx_bool init_gpu(FILE gmx_unused                 *fplog,
429                   int                              mygpu,
430                   char                            *result_str,
431                   const gmx_gpu_info_t gmx_unused *gpu_info,
432                   const gmx_gpu_opt_t             *gpu_opt
433                   )
434 {
435     assert(result_str);
436
437     result_str[0] = 0;
438
439     if (mygpu < 0 || mygpu >= gpu_opt->n_dev_use)
440     {
441         char        sbuf[STRLEN];
442         sprintf(sbuf, "Trying to initialize an inexistent GPU: "
443                 "there are %d %s-selected GPU(s), but #%d was requested.",
444                 gpu_opt->n_dev_use, gpu_opt->bUserSet ? "user" : "auto", mygpu);
445         gmx_incons(sbuf);
446     }
447
448     return TRUE;
449 }
450
451 //! This function is documented in the header file
452 int get_gpu_device_id(const gmx_gpu_info_t  *,
453                       const gmx_gpu_opt_t  *gpu_opt,
454                       int                   idx)
455 {
456     assert(gpu_opt);
457     assert(idx >= 0 && idx < gpu_opt->n_dev_use);
458
459     return gpu_opt->dev_use[idx];
460 }
461
462 //! This function is documented in the header file
463 char* get_ocl_gpu_device_name(const gmx_gpu_info_t *gpu_info,
464                               const gmx_gpu_opt_t  *gpu_opt,
465                               int                   idx)
466 {
467     assert(gpu_info);
468     assert(gpu_opt);
469     assert(idx >= 0 && idx < gpu_opt->n_dev_use);
470
471     return gpu_info->gpu_dev[gpu_opt->dev_use[idx]].device_name;
472 }
473
474 //! This function is documented in the header file
475 size_t sizeof_gpu_dev_info(void)
476 {
477     return sizeof(gmx_device_info_t);
478 }
479
480 /*! \brief Prints the name of a kernel function pointer.
481  *
482  * \param[in]    kernel   OpenCL kernel
483  * \returns               CL_SUCCESS if the operation was successful, an OpenCL error otherwise.
484  */
485 cl_int dbg_ocl_kernel_name(const cl_kernel kernel)
486 {
487     cl_int cl_error;
488     char   kernel_name[256];
489     cl_error = clGetKernelInfo(kernel, CL_KERNEL_FUNCTION_NAME,
490                                sizeof(kernel_name), &kernel_name, NULL);
491     if (cl_error)
492     {
493         printf("No kernel found!\n");
494     }
495     else
496     {
497         printf("%s\n", kernel_name);
498     }
499     return cl_error;
500 }
501
502 /*! \brief Prints the name of a kernel function pointer.
503  *
504  * \param[in]    kernel   OpenCL kernel
505  * \returns               CL_SUCCESS if the operation was successful, an OpenCL error otherwise.
506  */
507 cl_int dbg_ocl_kernel_name_address(void* kernel)
508 {
509     cl_int cl_error;
510     char   kernel_name[256];
511     cl_error = clGetKernelInfo((cl_kernel)kernel, CL_KERNEL_FUNCTION_NAME,
512                                sizeof(kernel_name), &kernel_name, NULL);
513     if (cl_error)
514     {
515         printf("No kernel found!\n");
516     }
517     else
518     {
519         printf("%s\n", kernel_name);
520     }
521     return cl_error;
522 }
523
524 void gpu_set_host_malloc_and_free(bool               bUseGpuKernels,
525                                   gmx_host_alloc_t **nb_alloc,
526                                   gmx_host_free_t  **nb_free)
527 {
528     if (bUseGpuKernels)
529     {
530         *nb_alloc = &ocl_pmalloc;
531         *nb_free  = &ocl_pfree;
532     }
533     else
534     {
535         *nb_alloc = NULL;
536         *nb_free  = NULL;
537     }
538 }