Fix handling of counter resets
[alexxy/gromacs.git] / cmake / gmxManageGPU.cmake
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 # If the user did not set GMX_GPU we'll consider this option to be
36 # in "auto" mode meaning that we will:
37 # - search for CUDA and set GMX_GPU=ON we find it
38 # - check whether GPUs are present
39 # - if CUDA is not found but GPUs were detected issue a warning
40 if (NOT DEFINED GMX_GPU)
41     set(GMX_GPU_AUTO TRUE CACHE INTERNAL "GPU acceleration will be selected automatically")
42 else()
43     set(GMX_GPU_AUTO FALSE CACHE INTERNAL "GPU acceleration will be selected automatically")
44 endif()
45 option(GMX_GPU "Enable GPU acceleration" OFF)
46
47 option(GMX_CLANG_CUDA "Use clang for CUDA" OFF)
48
49 if(GMX_GPU AND GMX_DOUBLE)
50     message(FATAL_ERROR "GPU acceleration is not available in double precision!")
51 endif()
52 if(GMX_GPU_AUTO AND GMX_DOUBLE)
53     message(WARNING "GPU acceleration is not available in double precision, disabled!")
54     set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
55     set_property(CACHE GMX_GPU_AUTO PROPERTY VALUE OFF)
56 endif()
57
58 # detect GPUs in the build host machine
59 if ((GMX_GPU OR GMX_GPU_AUTO) AND NOT GMX_GPU_DETECTION_DONE)
60     include(gmxDetectGpu)
61     gmx_detect_gpu()
62 endif()
63
64 # We need to call find_package even when we've already done the detection/setup
65 if(GMX_GPU OR GMX_GPU_AUTO)
66     if(NOT GMX_GPU AND NOT GMX_DETECT_GPU_AVAILABLE)
67         # Stay quiet when detection has occured and found no GPU.
68         # Noise is acceptable when there is a GPU or the user required one.
69         set(FIND_CUDA_QUIETLY QUIET)
70     endif()
71
72     # Cmake tries to use the static cuda runtime by default,
73     # but this leads to unusable GPU builds on OS X.
74     if(APPLE)
75         set(CUDA_USE_STATIC_CUDA_RUNTIME OFF CACHE STRING "Use the static version of the CUDA runtime library if available")
76     endif()
77
78     find_package(CUDA ${REQUIRED_CUDA_VERSION} ${FIND_CUDA_QUIETLY})
79 endif()
80
81 # Depending on the current vale of GMX_GPU and GMX_GPU_AUTO:
82 # - OFF, FALSE: Will skip this detection/setup.
83 # - OFF, TRUE : Will keep GMX_GPU=OFF if no CUDA is detected, but will assemble
84 #               a warning message which will be issued at the end of the
85 #               configuration if GPU(s) were found in the build system.
86 # - ON , FALSE: The user requested GPU build and this requires CUDA, so we will
87 #               fail if it is not available.
88 # - ON , TRUE : Can't happen (GMX_GPU=ON can only be user-set at this point)
89 if((GMX_GPU OR GMX_GPU_AUTO) AND NOT GMX_GPU_DETECTION_DONE)
90     if (EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
91         set(CUDA_FOUND TRUE CACHE INTERNAL "Whether the CUDA toolkit was found" FORCE)
92     else()
93         set(CUDA_FOUND FALSE CACHE INTERNAL "Whether the CUDA toolkit was found" FORCE)
94     endif()
95
96     # assemble warning/error message
97     if (GMX_DETECT_GPU_AVAILABLE)
98         set(_msg "${GMX_DETECT_GPU_COUNT} NVIDIA GPU(s) found in the system")
99
100         # append GPU names
101         if (NOT GMX_DETECT_GPU_INFO STREQUAL "")
102             set(_msg "${_msg}:")
103             foreach(gpu ${GMX_DETECT_GPU_INFO})
104                 set(_msg "${_msg}
105 ${gpu}")
106             endforeach()
107         endif()
108
109         # TODO remove the second part of the message when we'll have compute
110         # capability information from the detection.
111         set(_msg "${_msg}
112 Compute capability information not available, consult the NVIDIA website:
113 https://developer.nvidia.com/cuda-gpus")
114     endif()
115
116         set(CUDA_NOTFOUND_MESSAGE "mdrun supports native GPU acceleration on NVIDIA hardware with compute capability >= ${REQUIRED_CUDA_COMPUTE_CAPABILITY} (Fermi or later). This requires the NVIDIA CUDA toolkit, which was not found. Its location can be hinted by setting the CUDA_TOOLKIT_ROOT_DIR CMake option (does not work as an environment variable). The typical location would be /usr/local/cuda[-version]. Note that CPU or GPU acceleration can be selected at runtime.
117
118 ${_msg}")
119         unset(_msg)
120
121     if (NOT CUDA_FOUND)
122         if (GMX_GPU_AUTO)
123             # Disable GPU acceleration in auto mode
124             message(STATUS "No compatible CUDA toolkit found (v5.0+), disabling native GPU acceleration")
125             set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
126             set(CUDA_NOTFOUND_AUTO ON)
127         else()
128             # the user requested CUDA, but it wasn't found
129             message(FATAL_ERROR "${CUDA_NOTFOUND_MESSAGE}")
130         endif()
131     else()
132         if (GMX_GPU_AUTO)
133             message(STATUS "Enabling native GPU acceleration")
134             set_property(CACHE GMX_GPU PROPERTY VALUE ON)
135         endif()
136     endif() # NOT CUDA_FOUND
137 endif()
138
139 # Try to find NVML if a GPU accelerated binary should be build.
140 if (GMX_GPU)
141     if (DEFINED NVML_LIBRARY)
142         set(NVML_FIND_QUIETLY TRUE)
143     endif()
144     find_package(NVML)
145     # TODO Default to off, since linking is not implemented reliably
146     option(GMX_USE_NVML "Use NVML support for better CUDA performance" OFF)
147     mark_as_advanced(GMX_USE_NVML)
148     if(GMX_USE_NVML)
149         if(NVML_FOUND)
150             include_directories(SYSTEM ${NVML_INCLUDE_DIR})
151             set(HAVE_NVML 1)
152             list(APPEND GMX_EXTRA_LIBRARIES ${NVML_LIBRARY})
153         else()
154             message(FATAL_ERROR "NVML support was required, but was not detected. Please consult the install guide.")
155         endif()
156     endif()
157 endif()
158
159 # Annoyingly enough, FindCUDA leaves a few variables behind as non-advanced.
160 # We need to mark these advanced outside the conditional, otherwise, if the
161 # user turns GMX_GPU=OFF after a failed cmake pass, these variables will be
162 # left behind in the cache.
163 mark_as_advanced(CUDA_SDK_ROOT_DIR
164                  CUDA_USE_STATIC_CUDA_RUNTIME
165                  CUDA_dl_LIBRARY CUDA_rt_LIBRARY
166                  )
167 if(NOT GMX_GPU)
168     mark_as_advanced(CUDA_TOOLKIT_ROOT_DIR)
169     mark_as_advanced(CUDA_HOST_COMPILER)
170 endif()
171
172 # Try to execute ${CUDA_NVCC_EXECUTABLE} --version and set the output
173 # (or an error string) in the argument variable.
174 # Note that semicolon is used as separator for nvcc.
175 #
176 # Parameters:
177 #   COMPILER_INFO   - [output variable] string with compiler path, ID and
178 #                     some compiler-provided information
179 #   COMPILER_FLAGS  - [output variable] flags for the compiler
180 #
181 macro(get_cuda_compiler_info COMPILER_INFO COMPILER_FLAGS)
182     if(NOT GMX_CLANG_CUDA)
183         if(CUDA_NVCC_EXECUTABLE)
184
185             # Get the nvcc version string. This is multi-line, but since it is only 4 lines
186             # and might change in the future it is better to store than trying to parse out
187             # the version from the current format.
188             execute_process(COMMAND ${CUDA_NVCC_EXECUTABLE} --version
189                 RESULT_VARIABLE _nvcc_version_res
190                 OUTPUT_VARIABLE _nvcc_version_out
191                 ERROR_VARIABLE  _nvcc_version_err
192                 OUTPUT_STRIP_TRAILING_WHITESPACE)
193             if (${_nvcc_version_res} EQUAL 0)
194                 # Fix multi-line mess: Replace newline with ";" so we can use it in a define
195                 string(REPLACE "\n" ";" _nvcc_info_singleline ${_nvcc_version_out})
196                 SET(${COMPILER_INFO} "${CUDA_NVCC_EXECUTABLE} ${_nvcc_info_singleline}")
197                 string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type)
198                 SET(_compiler_flags "${CUDA_NVCC_FLAGS_${_build_type}}")
199                 if(CUDA_PROPAGATE_HOST_FLAGS)
200                     string(REGEX REPLACE "[ ]+" ";" _cxx_flags_nospace "${BUILD_CXXFLAGS}")
201                 endif()
202                 SET(${COMPILER_FLAGS} "${CUDA_NVCC_FLAGS}${CUDA_NVCC_FLAGS_${_build_type}}; ${_cxx_flags_nospace}")
203             else()
204                 SET(${COMPILER_INFO} "N/A")
205                 SET(${COMPILER_FLAGS} "N/A")
206             endif()
207         endif()
208     else()
209         # CXX compiler is the CUDA compiler
210         set(${COMPILER_INFO} "${CMAKE_CXX_COMPILER}  ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
211         # there are some extra flags
212         set(${COMPILER_FLAGS} "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${_build_type}} ${GMX_CUDA_CLANG_FLAGS}")
213     endif()
214 endmacro ()
215
216 macro(enable_multiple_cuda_compilation_units)
217     message(STATUS "Enabling multiple compilation units for the CUDA non-bonded module.")
218     set_property(CACHE GMX_CUDA_NB_SINGLE_COMPILATION_UNIT PROPERTY VALUE OFF)
219 endmacro()
220
221 include(CMakeDependentOption)
222 include(gmxOptionUtilities)
223 macro(gmx_gpu_setup)
224     if(GMX_GPU)
225         if(NOT GMX_CLANG_CUDA)
226             if(NOT CUDA_NVCC_EXECUTABLE)
227                 message(FATAL_ERROR "nvcc is required for a CUDA build, please set CUDA_TOOLKIT_ROOT_DIR appropriately")
228             endif()
229             # set up nvcc options
230             include(gmxManageNvccConfig)
231         else()
232             include(gmxManageClangCudaConfig)
233         endif()
234
235         gmx_check_if_changed(_cuda_version_changed CUDA_VERSION)
236
237         # Generate CUDA RT API version string which will end up in config.h
238         # We do this because nvcc is silly enough to not define its own version
239         # (which should match the CUDA runtime API version AFAICT) and we want to
240         # avoid creating the fragile dependency on cuda_runtime_api.h.
241         #
242         # NOTE: CUDA v7.5 is expected to have nvcc define it own version, so in the
243         # future we should switch to using that version string instead of our own.
244         if (NOT GMX_CUDA_VERSION OR _cuda_version_changed)
245             MATH(EXPR GMX_CUDA_VERSION "${CUDA_VERSION_MAJOR}*1000 + ${CUDA_VERSION_MINOR}*10")
246         endif()
247
248         if (_cuda_version_changed)
249             # check the generated CUDA API version against the one present in cuda_runtime_api.h
250             try_compile(_get_cuda_version_compile_res
251                 ${CMAKE_BINARY_DIR}
252                 ${CMAKE_SOURCE_DIR}/cmake/TestCUDAVersion.c
253                 COMPILE_DEFINITIONS "-DGMX_CUDA_VERSION=${GMX_CUDA_VERSION}"
254                 CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CUDA_TOOLKIT_INCLUDE}"
255                 OUTPUT_VARIABLE _get_cuda_version_compile_out)
256
257             if (NOT _get_cuda_version_compile_res)
258                 if (_get_cuda_version_compile_out MATCHES "CUDA version mismatch")
259                     message(FATAL_ERROR "The CUDA API version generated internally from the compiler version does not match the version reported by cuda.h. This means either that the CUDA detection picked up mismatching nvcc and the CUDA headers (likely not part of the same toolkit installation) or that there is an error in the internal version generation. If you are sure that it is not the former causing the error (check the relevant cache variables), define the GMX_CUDA_VERSION cache variable to work around the error.")
260                 else()
261                     message(FATAL_ERROR "Could not detect CUDA runtime API version")
262                 endif()
263             endif()
264         endif()
265
266         # no OpenMP is no good!
267         if(NOT GMX_OPENMP)
268             message(WARNING "To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading. Without OpenMP a single CPU core can be used with a GPU which is not optimal. Note that with MPI multiple processes can be forced to use a single GPU, but this is typically inefficient. You need to set both C and C++ compilers that support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
269         endif()
270
271         if(NOT GMX_CLANG_CUDA)
272             gmx_check_if_changed(GMX_CHECK_NVCC CUDA_NVCC_EXECUTABLE CUDA_HOST_COMPILER CUDA_NVCC_FLAGS)
273
274             if(GMX_CHECK_NVCC OR NOT GMX_NVCC_WORKS)
275                 message(STATUS "Check for working NVCC/C compiler combination")
276                 execute_process(COMMAND ${CUDA_NVCC_EXECUTABLE} -ccbin ${CUDA_HOST_COMPILER} -c ${CUDA_NVCC_FLAGS} ${CMAKE_SOURCE_DIR}/cmake/TestCUDA.cu
277                                 RESULT_VARIABLE _cuda_test_res
278                                 OUTPUT_VARIABLE _cuda_test_out
279                                 ERROR_VARIABLE  _cuda_test_err
280                                 OUTPUT_STRIP_TRAILING_WHITESPACE)
281
282                 if(${_cuda_test_res})
283                     message(STATUS "Check for working NVCC/C compiler combination - broken")
284                     if(${_cuda_test_err} MATCHES "nsupported")
285                         message(FATAL_ERROR "NVCC/C compiler combination does not seem to be supported. CUDA frequently does not support the latest versions of the host compiler, so you might want to try an earlier C/C++ compiler version and make sure your CUDA compiler and driver are as recent as possible.")
286                     else()
287                         message(FATAL_ERROR "CUDA compiler does not seem to be functional.")
288                     endif()
289                 elseif(NOT GMX_CUDA_TEST_COMPILER_QUIETLY)
290                     message(STATUS "Check for working NVCC/C compiler combination - works")
291                     set(GMX_NVCC_WORKS TRUE CACHE INTERNAL "Nvcc can compile a trivial test program")
292                 endif()
293             endif() # GMX_CHECK_NVCC
294         endif() #GMX_CLANG_CUDA
295     endif() # GMX_GPU
296
297     option(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT "Whether to compile the CUDA non-bonded module using a single compilation unit." OFF)
298     mark_as_advanced(GMX_CUDA_NB_SINGLE_COMPILATION_UNIT)
299
300 endmacro()