Make PME OpenCL enabled only for AMD devices
[alexxy/gromacs.git] / src / gromacs / 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,2016,2017,2018, 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 "config.h"
46
47 #include <assert.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #ifdef __APPLE__
52 #    include <sys/sysctl.h>
53 #endif
54
55 #include <memory.h>
56
57 #include "gromacs/gpu_utils/gpu_utils.h"
58 #include "gromacs/gpu_utils/ocl_compiler.h"
59 #include "gromacs/gpu_utils/oclutils.h"
60 #include "gromacs/hardware/hw_info.h"
61 #include "gromacs/utility/cstringutil.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/smalloc.h"
65 #include "gromacs/utility/stringutil.h"
66
67 /*! \brief Return true if executing on compatible OS for AMD OpenCL.
68  *
69  * This is assumed to be true for OS X version of at least 10.10.4 and
70  * all other OS flavors.
71  *
72  * Uses the BSD sysctl() interfaces to extract the kernel version.
73  *
74  * \return true if version is 14.4 or later (= OS X version 10.10.4),
75  *         or OS is not Darwin.
76  */
77 static bool
78 runningOnCompatibleOSForAmd()
79 {
80 #ifdef __APPLE__
81     int    mib[2];
82     char   kernelVersion[256];
83     size_t len = sizeof(kernelVersion);
84
85     mib[0] = CTL_KERN;
86     mib[1] = KERN_OSRELEASE;
87
88     sysctl(mib, sizeof(mib)/sizeof(mib[0]), kernelVersion, &len, NULL, 0);
89
90     int major = strtod(kernelVersion, NULL);
91     int minor = strtod(strchr(kernelVersion, '.')+1, NULL);
92
93     // Kernel 14.4 corresponds to OS X 10.10.4
94     return (major > 14 || (major == 14 && minor >= 4));
95 #else
96     return true;
97 #endif
98 }
99
100 /*! \brief Returns true if the gpu characterized by the device properties is
101  *  supported by the native gpu acceleration.
102  * \returns             true if the GPU properties passed indicate a compatible
103  *                      GPU, otherwise false.
104  */
105 static int is_gmx_supported_gpu_id(gmx_device_info_t *ocl_gpu_device)
106 {
107     if ((getenv("GMX_OCL_DISABLE_COMPATIBILITY_CHECK")) != nullptr)
108     {
109         return egpuCompatible;
110     }
111
112     /* Only AMD, Intel, and NVIDIA GPUs are supported for now */
113     switch (ocl_gpu_device->vendor_e)
114     {
115         case OCL_VENDOR_NVIDIA:
116             return egpuCompatible;
117         case OCL_VENDOR_AMD:
118             return runningOnCompatibleOSForAmd() ? egpuCompatible : egpuIncompatible;
119         case OCL_VENDOR_INTEL:
120             return GMX_OPENCL_NB_CLUSTER_SIZE == 4 ? egpuCompatible : egpuIncompatibleClusterSize;
121         default:
122             return egpuIncompatible;
123     }
124 }
125
126
127 /*! \brief Returns an ocl_vendor_id_t value corresponding to the input OpenCL vendor name.
128  *
129  *  \param[in] vendor_name String with OpenCL vendor name.
130  *  \returns               ocl_vendor_id_t value for the input vendor_name
131  */
132 static ocl_vendor_id_t get_vendor_id(char *vendor_name)
133 {
134     if (vendor_name)
135     {
136         if (strstr(vendor_name, "NVIDIA"))
137         {
138             return OCL_VENDOR_NVIDIA;
139         }
140         else
141         if (strstr(vendor_name, "AMD") ||
142             strstr(vendor_name, "Advanced Micro Devices"))
143         {
144             return OCL_VENDOR_AMD;
145         }
146         else
147         if (strstr(vendor_name, "Intel"))
148         {
149             return OCL_VENDOR_INTEL;
150         }
151     }
152     return OCL_VENDOR_UNKNOWN;
153 }
154
155
156 //! This function is documented in the header file
157 bool canDetectGpus(std::string *errorMessage)
158 {
159     cl_uint numPlatforms;
160     cl_int  status       = clGetPlatformIDs(0, nullptr, &numPlatforms);
161     GMX_ASSERT(status != CL_INVALID_VALUE, "Incorrect call of clGetPlatformIDs detected");
162 #ifdef cl_khr_icd
163     if (status == CL_PLATFORM_NOT_FOUND_KHR)
164     {
165         // No valid ICDs found
166         if (errorMessage != nullptr)
167         {
168             errorMessage->assign("No valid OpenCL driver found");
169         }
170         return false;
171     }
172 #endif
173     GMX_RELEASE_ASSERT(status == CL_SUCCESS,
174                        gmx::formatString("An unexpected value was returned from clGetPlatformIDs %d: %s",
175                                          status, ocl_get_error_string(status).c_str()).c_str());
176     bool foundPlatform = (numPlatforms > 0);
177     if (!foundPlatform && errorMessage != nullptr)
178     {
179         errorMessage->assign("No OpenCL platforms found even though the driver was valid");
180     }
181     return foundPlatform;
182 }
183
184 //! This function is documented in the header file
185 void findGpus(gmx_gpu_info_t *gpu_info)
186 {
187     cl_uint         ocl_platform_count;
188     cl_platform_id *ocl_platform_ids;
189     cl_device_type  req_dev_type = CL_DEVICE_TYPE_GPU;
190
191     ocl_platform_ids = nullptr;
192
193     if (getenv("GMX_OCL_FORCE_CPU") != nullptr)
194     {
195         req_dev_type = CL_DEVICE_TYPE_CPU;
196     }
197
198     while (true)
199     {
200         cl_int status = clGetPlatformIDs(0, nullptr, &ocl_platform_count);
201         if (CL_SUCCESS != status)
202         {
203             GMX_THROW(gmx::InternalError(gmx::formatString("An unexpected value %d was returned from clGetPlatformIDs: ",
204                                                            status) + ocl_get_error_string(status)));
205         }
206
207         if (1 > ocl_platform_count)
208         {
209             // TODO this should have a descriptive error message that we only support one OpenCL platform
210             break;
211         }
212
213         snew(ocl_platform_ids, ocl_platform_count);
214
215         status = clGetPlatformIDs(ocl_platform_count, ocl_platform_ids, nullptr);
216         if (CL_SUCCESS != status)
217         {
218             GMX_THROW(gmx::InternalError(gmx::formatString("An unexpected value %d was returned from clGetPlatformIDs: ",
219                                                            status) + ocl_get_error_string(status)));
220         }
221
222         for (unsigned int i = 0; i < ocl_platform_count; i++)
223         {
224             cl_uint ocl_device_count;
225
226             /* If requesting req_dev_type devices fails, just go to the next platform */
227             if (CL_SUCCESS != clGetDeviceIDs(ocl_platform_ids[i], req_dev_type, 0, nullptr, &ocl_device_count))
228             {
229                 continue;
230             }
231
232             if (1 <= ocl_device_count)
233             {
234                 gpu_info->n_dev += ocl_device_count;
235             }
236         }
237
238         if (1 > gpu_info->n_dev)
239         {
240             break;
241         }
242
243         snew(gpu_info->gpu_dev, gpu_info->n_dev);
244
245         {
246             int           device_index;
247             cl_device_id *ocl_device_ids;
248
249             snew(ocl_device_ids, gpu_info->n_dev);
250             device_index = 0;
251
252             for (unsigned int i = 0; i < ocl_platform_count; i++)
253             {
254                 cl_uint ocl_device_count;
255
256                 /* If requesting req_dev_type devices fails, just go to the next platform */
257                 if (CL_SUCCESS != clGetDeviceIDs(ocl_platform_ids[i], req_dev_type, gpu_info->n_dev, ocl_device_ids, &ocl_device_count))
258                 {
259                     continue;
260                 }
261
262                 if (1 > ocl_device_count)
263                 {
264                     break;
265                 }
266
267                 for (unsigned int j = 0; j < ocl_device_count; j++)
268                 {
269                     gpu_info->gpu_dev[device_index].ocl_gpu_id.ocl_platform_id = ocl_platform_ids[i];
270                     gpu_info->gpu_dev[device_index].ocl_gpu_id.ocl_device_id   = ocl_device_ids[j];
271
272                     gpu_info->gpu_dev[device_index].device_name[0] = 0;
273                     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, nullptr);
274
275                     gpu_info->gpu_dev[device_index].device_version[0] = 0;
276                     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, nullptr);
277
278                     gpu_info->gpu_dev[device_index].device_vendor[0] = 0;
279                     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, nullptr);
280
281                     gpu_info->gpu_dev[device_index].compute_units = 0;
282                     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), nullptr);
283
284                     gpu_info->gpu_dev[device_index].adress_bits = 0;
285                     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), nullptr);
286
287                     gpu_info->gpu_dev[device_index].vendor_e = get_vendor_id(gpu_info->gpu_dev[device_index].device_vendor);
288
289                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_MAX_WORK_ITEM_SIZES, 3 * sizeof(size_t), &gpu_info->gpu_dev[device_index].maxWorkItemSizes, nullptr);
290
291                     clGetDeviceInfo(ocl_device_ids[j], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &gpu_info->gpu_dev[device_index].maxWorkGroupSize, nullptr);
292
293                     gpu_info->gpu_dev[device_index].stat = is_gmx_supported_gpu_id(gpu_info->gpu_dev + device_index);
294
295                     if (egpuCompatible == gpu_info->gpu_dev[device_index].stat)
296                     {
297                         gpu_info->n_dev_compatible++;
298                     }
299
300                     device_index++;
301                 }
302             }
303
304             gpu_info->n_dev = device_index;
305
306             /* Dummy sort of devices -  AMD first, then NVIDIA, then Intel */
307             // TODO: Sort devices based on performance.
308             if (0 < gpu_info->n_dev)
309             {
310                 int last = -1;
311                 for (int i = 0; i < gpu_info->n_dev; i++)
312                 {
313                     if (OCL_VENDOR_AMD == gpu_info->gpu_dev[i].vendor_e)
314                     {
315                         last++;
316
317                         if (last < i)
318                         {
319                             gmx_device_info_t ocl_gpu_info;
320                             ocl_gpu_info            = gpu_info->gpu_dev[i];
321                             gpu_info->gpu_dev[i]    = gpu_info->gpu_dev[last];
322                             gpu_info->gpu_dev[last] = ocl_gpu_info;
323                         }
324                     }
325                 }
326
327                 /* if more than 1 device left to be sorted */
328                 if ((gpu_info->n_dev - 1 - last) > 1)
329                 {
330                     for (int i = 0; i < gpu_info->n_dev; i++)
331                     {
332                         if (OCL_VENDOR_NVIDIA == gpu_info->gpu_dev[i].vendor_e)
333                         {
334                             last++;
335
336                             if (last < i)
337                             {
338                                 gmx_device_info_t ocl_gpu_info;
339                                 ocl_gpu_info            = gpu_info->gpu_dev[i];
340                                 gpu_info->gpu_dev[i]    = gpu_info->gpu_dev[last];
341                                 gpu_info->gpu_dev[last] = ocl_gpu_info;
342                             }
343                         }
344                     }
345                 }
346             }
347
348             sfree(ocl_device_ids);
349         }
350
351         break;
352     }
353
354     sfree(ocl_platform_ids);
355 }
356
357 //! This function is documented in the header file
358 void get_gpu_device_info_string(char *s, const gmx_gpu_info_t &gpu_info, int index)
359 {
360     assert(s);
361
362     if (index < 0 && index >= gpu_info.n_dev)
363     {
364         return;
365     }
366
367     gmx_device_info_t *dinfo = &gpu_info.gpu_dev[index];
368
369     bool               bGpuExists = (dinfo->stat != egpuNonexistent &&
370                                      dinfo->stat != egpuInsane);
371
372     if (!bGpuExists)
373     {
374         sprintf(s, "#%d: %s, stat: %s",
375                 index, "N/A",
376                 gpu_detect_res_str[dinfo->stat]);
377     }
378     else
379     {
380         sprintf(s, "#%d: name: %s, vendor: %s, device version: %s, stat: %s",
381                 index, dinfo->device_name, dinfo->device_vendor,
382                 dinfo->device_version,
383                 gpu_detect_res_str[dinfo->stat]);
384     }
385 }
386
387 bool areAllGpuDevicesFromAmd(const gmx_gpu_info_t &gpuInfo)
388 {
389     bool result = true;
390     for (int i = 0; i < gpuInfo.n_dev; ++i)
391     {
392         if ((gpuInfo.gpu_dev[i].stat == egpuCompatible) &&
393             (gpuInfo.gpu_dev[i].vendor_e != OCL_VENDOR_AMD))
394         {
395             result = false;
396             break;
397         }
398     }
399     return result;
400 }
401
402 //! This function is documented in the header file
403 void init_gpu(const gmx_device_info_t *deviceInfo)
404 {
405     assert(deviceInfo);
406
407     // If the device is NVIDIA, for safety reasons we disable the JIT
408     // caching as this is known to be broken at least until driver 364.19;
409     // the cache does not always get regenerated when the source code changes,
410     // e.g. if the path to the kernel sources remains the same
411
412     if (deviceInfo->vendor_e == OCL_VENDOR_NVIDIA)
413     {
414         // Ignore return values, failing to set the variable does not mean
415         // that something will go wrong later.
416 #ifdef _MSC_VER
417         _putenv("CUDA_CACHE_DISABLE=1");
418 #else
419         // Don't override, maybe a dev is testing.
420         setenv("CUDA_CACHE_DISABLE", "1", 0);
421 #endif
422     }
423 }
424
425 //! This function is documented in the header file
426 gmx_device_info_t *getDeviceInfo(const gmx_gpu_info_t &gpu_info,
427                                  int                   deviceId)
428 {
429     if (deviceId < 0 || deviceId >= gpu_info.n_dev)
430     {
431         gmx_incons("Invalid GPU deviceId requested");
432     }
433     return &gpu_info.gpu_dev[deviceId];
434 }
435
436 //! This function is documented in the header file
437 size_t sizeof_gpu_dev_info()
438 {
439     return sizeof(gmx_device_info_t);
440 }
441
442 void gpu_set_host_malloc_and_free(bool               bUseGpuKernels,
443                                   gmx_host_alloc_t **nb_alloc,
444                                   gmx_host_free_t  **nb_free)
445 {
446     if (bUseGpuKernels)
447     {
448         *nb_alloc = &pmalloc;
449         *nb_free  = &pfree;
450     }
451     else
452     {
453         *nb_alloc = nullptr;
454         *nb_free  = nullptr;
455     }
456 }
457
458 int gpu_info_get_stat(const gmx_gpu_info_t &info, int index)
459 {
460     return info.gpu_dev[index].stat;
461 }