Minor fixes to GPU build system
[gromacs.git] / cmake / gmxDetectGpu.cmake
blob7ccdade28134d371578bfaf6767e6f1f01d4a3fe
2 # This file is part of the GROMACS molecular simulation package.
4 # Copyright (c) 2012,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.
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.
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.
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.
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.
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.
35 # The gmx_detect_gpu() macro aims to detect GPUs available in the build machine
36 # and provide the number, names, and compute-capabilities of these devices.
38 # The current version is limited to checking the availability of NVIDIA GPUs
39 # without compute-capability information.
41 # The current detection relies on the following checks in the order of listing:
42 # - output of nvidia-smi (if available);
43 # - presence and content of of /proc/driver/nvidia/gpus/*/information (Linux)
44 # - output of lspci (Linux)
46 # If any of the checks succeeds in finding devices, consecutive checks will not
47 # be carried out. Additionally, when lspci is used and a device with unknown
48 # PCI ID is encountered, lspci tries to check the online PCI ID database. If
49 # this is not possible or the device is simply not recognized, no device names
50 # will be available.
52 # The following advanced variables are defined:
53 # - GMX_DETECT_GPU_AVAILABLE - TRUE if any GPUs were detected, otherwise FALSE
54 # - GMX_DETECT_GPU_COUNT     - # of GPUs detected
55 # - GMX_DETECT_GPU_INFO      - list of information strings of the detected GPUs
57 # NOTE: The proper solution is to detect hardware compatible with the native
58 # GPU acceleration. However, this requires checking the compute capability
59 # of the device which is not possible with the current checks and requires
60 # interfacing with the CUDA driver API.
63 # check whether the number of GPUs machetes the number of elements in the GPU info list
64 macro(check_num_gpu_info NGPU GPU_INFO)
65     list(LENGTH ${GPU_INFO} _len)
66     if (NOT NGPU EQUAL _len)
67         list(APPEND ${GMX_DETECT_GPU_INFO} "NOTE: information about some GPU(s) missing!")
68     endif()
69 endmacro()
71 macro(gmx_detect_gpu)
73     if (NOT DEFINED GMX_DETECT_GPU_AVAILABLE OR
74         NOT DEFINED GMX_DETECT_GPU_COUNT OR
75         NOT DEFINED GMX_DETECT_GPU_INFO)
77         set(GMX_DETECT_GPU_COUNT 0)
78         set(GMX_DETECT_GPU_INFO  "")
80         message(STATUS "Looking for NVIDIA GPUs present in the system")
82         # nvidia-smi-based detection.
83         # Requires the nvidia-smi tool to be installed and available in the path
84         # or in one of the default search locations
85         if (NOT DEFINED GMX_DETECT_GPU_COUNT_NVIDIA_SMI)
86             # try to find the nvidia-smi binary
87             # TODO add location hints
88             find_program(_nvidia_smi "nvidia-smi")
89             if (_nvidia_smi)
90                 set(GMX_DETECT_GPU_COUNT_NVIDIA_SMI 0)
91                 # execute nvidia-smi -L to get a short list of GPUs available
92                 exec_program(${_nvidia_smi_path} ARGS -L
93                     OUTPUT_VARIABLE _nvidia_smi_out
94                     RETURN_VALUE    _nvidia_smi_ret)
95                 # process the stdout of nvidia-smi
96                 if (_nvidia_smi_ret EQUAL 0)
97                     # convert string with newlines to list of strings
98                     string(REGEX REPLACE "\n" ";" _nvidia_smi_out "${_nvidia_smi_out}")
99                     foreach(_line ${_nvidia_smi_out})
100                         if (_line MATCHES "^GPU [0-9]+:")
101                             math(EXPR GMX_DETECT_GPU_COUNT_NVIDIA_SMI "${GMX_DETECT_GPU_COUNT_NVIDIA_SMI}+1")
102                             # the UUID is not very useful for the user, remove it
103                             string(REGEX REPLACE " \\(UUID:.*\\)" "" _gpu_info "${_line}")
104                             if (NOT _gpu_info STREQUAL "")
105                                 list(APPEND GMX_DETECT_GPU_INFO "${_gpu_info}")
106                             endif()
107                         endif()
108                     endforeach()
110                     check_num_gpu_info(${GMX_DETECT_GPU_COUNT_NVIDIA_SMI} GMX_DETECT_GPU_INFO)
111                     set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_NVIDIA_SMI})
112                 endif()
113             endif()
115             unset(_nvidia_smi CACHE)
116             unset(_nvidia_smi_ret)
117             unset(_nvidia_smi_out)
118             unset(_gpu_name)
119             unset(_line)
120         endif()
122         if (UNIX AND NOT (APPLE OR CYGWIN))
123             # /proc/driver/nvidia/gpus/*/information-based detection.
124             # Requires the NVDIA closed source driver to be installed and loaded
125             if (NOT DEFINED GMX_DETECT_GPU_COUNT_PROC AND GMX_DETECT_GPU_COUNT EQUAL 0)
126                 set(GMX_DETECT_GPU_COUNT_PROC 0)
127                 file(GLOB _proc_nv_gpu_info "/proc/driver/nvidia/gpus/*/information")
128                 foreach (_file ${_proc_nv_gpu_info})
129                     math(EXPR GMX_DETECT_GPU_COUNT_PROC "${GMX_DETECT_GPU_COUNT_PROC}+1")
130                     # assemble information strings similar to the nvidia-smi output
131                     # GPU ID = directory name on /proc/driver/nvidia/gpus/
132                     string(REGEX REPLACE "/proc/driver/nvidia/gpus.*([0-9]+).*information" "\\1" _gpu_id ${_file})
133                     # GPU name
134                     file(STRINGS ${_file} _gpu_name LIMIT_COUNT 1 REGEX "^Model:.*" NO_HEX_CONVERSION)
135                     string(REGEX REPLACE "^Model:[ \t]*(.*)" "\\1" _gpu_name "${_gpu_name}")
136                     if (NOT _gpu_id STREQUAL "" AND NOT _gpu_name STREQUAL "")
137                         list(APPEND GMX_DETECT_GPU_INFO "GPU ${_gpu_id}: ${_gpu_name}")
138                     endif()
139                 endforeach()
141                 check_num_gpu_info(${GMX_DETECT_GPU_COUNT_PROC} GMX_DETECT_GPU_INFO)
142                 set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_PROC})
144                 unset(_proc_nv_gpu_info)
145                 unset(_gpu_name)
146                 unset(_gpu_id)
147                 unset(_file)
148             endif()
150             # lspci-based detection (does not provide GPU information).
151             # Requires lspci and for GPU names to be fetched from the central
152             # PCI ID db if not available locally.
153             if (NOT DEFINED GMX_DETECT_GPU_COUNT_LSPCI AND GMX_DETECT_GPU_COUNT EQUAL 0)
154                 set(GMX_DETECT_GPU_COUNT_LSPCI 0)
155                 exec_program(lspci ARGS -q
156                     OUTPUT_VARIABLE _lspci_out
157                     RETURN_VALUE    _lspci_ret)
158                 # prehaps -q is not supported, try running without
159                 if (NOT RETURN_VALUE EQUAL 0)
160                     exec_program(lspci
161                         OUTPUT_VARIABLE _lspci_out
162                         RETURN_VALUE    _lspci_ret)
163                 endif()
164                 if (_lspci_ret EQUAL 0)
165                     # convert string with newlines to list of strings
166                     STRING(REGEX REPLACE ";" "\\\\;" _lspci_out "${_lspci_out}")
167                     string(REGEX REPLACE "\n" ";" _lspci_out "${_lspci_out}")
168                     foreach(_line ${_lspci_out})
169                         string(TOUPPER "${_line}" _line_upper)
170                         if (_line_upper MATCHES ".*VGA.*NVIDIA.*" OR _line_upper MATCHES ".*3D.*NVIDIA.*")
171                             math(EXPR GMX_DETECT_GPU_COUNT_LSPCI "${GMX_DETECT_GPU_COUNT_LSPCI}+1")
172                             # Try to parse out the device name which should be
173                             # included in the lspci -q output between []-s
174                             string(REGEX REPLACE ".*\\[(.*)\\].*" "\\1" _gpu_name "${_line}")
175                             if (NOT _gpu_name EQUAL "")
176                                 list(APPEND GMX_DETECT_GPU_INFO "${_gpu_name}")
177                             endif()
178                         endif()
179                     endforeach()
181                     check_num_gpu_info(${GMX_DETECT_GPU_COUNT_LSPCI} GMX_DETECT_GPU_INFO)
182                     set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_LSPCI})
183                 endif()
185                 unset(_lspci_ret)
186                 unset(_lspci_out)
187                 unset(_gpu_name)
188                 unset(_line)
189                 unset(_line_upper)
190             endif()
191         endif()
193         if (GMX_DETECT_GPU_COUNT GREATER 0)
194             set(GMX_DETECT_GPU_AVAILABLE YES)
195         else()
196             set(GMX_DETECT_GPU_AVAILABLE NO)
197         endif()
198         set(GMX_DETECT_GPU_AVAILABLE ${GMX_DETECT_GPU_AVAILABLE} CACHE BOOL "Whether any NVIDIA GPU was detected" FORCE)
200         set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT}
201             CACHE STRING "Number of NVIDIA GPUs detected")
202         set(GMX_DETECT_GPU_INFO ${GMX_DETECT_GPU_INFO}
203             CACHE STRING "basic information on the detected NVIDIA GPUs")
205         set(GMX_DETECT_GPU_COUNT_NVIDIA_SMI ${GMX_DETECT_GPU_COUNT_NVIDIA_SMI}
206             CACHE INTERNAL "Number of NVIDIA GPUs detected using nvidia-smi")
207         set(GMX_DETECT_GPU_COUNT_PROC ${GMX_DETECT_GPU_COUNT_PROC}
208             CACHE INTERNAL "Number of NVIDIA GPUs detected in /proc/driver/nvidia/gpus")
209         set(GMX_DETECT_GPU_COUNT_LSPCI ${GMX_DETECT_GPU_COUNT_LSPCI}
210             CACHE INTERNAL "Number of NVIDIA GPUs detected using lspci")
212         mark_as_advanced(GMX_DETECT_GPU_AVAILABLE
213                          GMX_DETECT_GPU_COUNT
214                          GMX_DETECT_GPU_INFO)
216         if (GMX_DETECT_GPU_AVAILABLE)
217             message(STATUS "Number of NVIDIA GPUs detected: ${GMX_DETECT_GPU_COUNT} ")
218         else()
219             message(STATUS "Could not detect NVIDIA GPUs")
220         endif()
222     endif()
223 endmacro(gmx_detect_gpu)