########################################################################
# User input options - enable C++ - before any CXX flags are changed #
########################################################################
-option(GMX_GPU "Enable GPU acceleration" ON)
+
+# decide on GPU settings based on user-settings and GPU/CUDA detection
+include(gmxManageGPU)
+
option(GMX_OPENMM "Accelerated execution on GPUs through the OpenMM library (rerun cmake after changing to see relevant options)" OFF)
option(GMX_FORCE_CXX "Enable C++ compilation even if not necessary" OFF)
mark_as_advanced(GMX_FORCE_CXX)
+
if(GMX_GPU OR GMX_OPENMM OR GMX_FORCE_CXX)
enable_language(CXX)
endif()
find_package(OpenMM)
endif(GMX_OPENMM)
-
if(GMX_GPU)
- if(GMX_DOUBLE)
- message(WARNING "GPU acceleration is not available in double precision, disabled!")
- set(GMX_GPU OFF CACHE BOOL "Enable GPU acceleration" FORCE)
- endif()
+ # now that we have detected the dependencies, do the second configure pass
+ gmx_gpu_setup()
endif(GMX_GPU)
-if(GMX_GPU)
-
- # We support CUDA >=v3.2 on *nix, but <= v4.1 doesn't work with MSVC
- if(MSVC)
- find_package(CUDA 4.1)
- else()
- find_package(CUDA 3.2)
- endif()
-
- if (NOT EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
- message(FATAL_ERROR "
- mdrun supports native GPU acceleration on NVIDIA hardware with compute
- capability >=2.0. This requires the NVIDIA CUDA library, which was not
- found; the location can be hinted by setting CUDA_TOOLKIT_ROOT_DIR as
- a CMake option (It does not work as an environment variable).
- The typical location would be /usr/local/cuda.
-
- CPU or GPU acceleration can be selected at runtime, but if you are
- sure you can not make use of GPU acceleration, disable it by setting
- the CMake variable GMX_GPU=OFF.")
- endif()
-
- if(NOT GMX_OPENMP)
- message(WARNING "
- In order to use GPU acceleration efficiently, mdrun requires OpenMP multithreading.
- Without OpenMP only a single CPU core per GPU can be used which is suboptimal.
- Note that with MPI multiple processes can be forced to use a single GPU, but this
- typically inefficient. Note that you need to set both C and C++ compilers that
- support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
- endif()
-
- include(gmxManageNvccConfig)
-
- # Check whether we can use atomic operations needed for polling wait for GPU
- # (to avoid the cudaStreamSynchronize + ECC bug).
- # With thread-MPI testing atomics has already been carried out, but without
- # thread-MPI we need to invoke the atomics test independently.
- if (NOT GMX_THREAD_MPI)
- set(TEST_TMPI_ATOMICS_ONLY ON CACHE INTERNAL
- "Test only the atomic operations of thread-MPI.")
- include(ThreadMPI)
- endif()
-
- # Version info (semicolon used as line separator) for nvcc.
- get_nvcc_version_info()
-
-endif()
-# Annoyingly enough, FindCUDA leaves a few variables behind as non-advanced.
-# We need to mark these advanced outside the conditional, otherwise, if the user
-# tuns GMX_GPU=OFF after a failed cmake pass, these variables will be left behind.
-mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_SDK_ROOT_DIR CUDA_VERBOSE_BUILD)
-
if(APPLE)
find_library(ACCELERATE_FRAMEWORK Accelerate)
list(APPEND GMX_EXTRA_LIBRARIES ${ACCELERATE_FRAMEWORK})
add_subdirectory(src)
add_subdirectory(scripts)
+# issue GPU acceleration/CUDA-related warning or error if GMX_GPU was set
+# Auto and NVIDIA GPUs were detected.
+if (CUDA_NOTFOUND_AUTO AND NOT GMX_GPU_DETECTION_DONE)
+ message(WARNING "${CUDA_NOTFOUND_MESSAGE}")
+ unset(CUDA_NOTFOUND_AUTO)
+ unset(CUDA_NOTFOUND_MESSAGE)
+endif()
+set(GMX_GPU_DETECTION_DONE TRUE CACHE INTERNAL "Whether GPU detection has already been done")
+
#######################
## uninstall target
#######################
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake")
###########################
-
########################################################################
# Tests #
########################################################################
--- /dev/null
+# The gmx_detect_gpu() macro aims to detect GPUs available in the build machine
+# and provide the number, names, and compute-capabilities of these devices.
+#
+# The current version is limited to checking the availability of NVIDIA GPUs
+# without compute-capability information.
+#
+# The current detection relies on the following checks in the order of listing:
+# - output of nvidia-smi (if available);
+# - presence and content of of /proc/driver/nvidia/gpus/*/information (Linux)
+# - output of lspci (Linux)
+#
+# If any of the checks succeeds in finding devices, consecutive checks will not
+# be carried out. Additionally, when lspci is used and a device with unknown
+# PCI ID is encountered, lspci tries to check the online PCI ID database. If
+# this is not possible or the device is simply not recognized, no device names
+# will be available.
+#
+# The following advanced variables are defined:
+# - GMX_DETECT_GPU_AVAILABLE - TRUE if any GPUs were detected, otherwise FALSE
+# - GMX_DETECT_GPU_COUNT - # of GPUs detected
+# - GMX_DETECT_GPU_INFO - list of information strings of the detected GPUs
+#
+# NOTE: The proper solution is to detect hardware compatible with the native
+# GPU acceleration. However, this requires checking the compute capability
+# of the device which is not possible with the current checks and requires
+# interfacing with the CUDA driver API.
+#
+
+# check whether the number of GPUs machetes the number of elements in the GPU info list
+macro(check_num_gpu_info NGPU GPU_INFO)
+ list(LENGTH ${GPU_INFO} _len)
+ if (NOT NGPU EQUAL _len)
+ list(APPEND ${GMX_DETECT_GPU_INFO} "NOTE: information about some GPU(s) missing!")
+ endif()
+endmacro()
+
+macro(gmx_detect_gpu)
+
+ if (NOT DEFINED GMX_DETECT_GPU_COUNT OR NOT DEFINED GMX_DETECT_GPU_INFO)
+
+ set(GMX_DETECT_GPU_COUNT 0)
+ set(GMX_DETECT_GPU_INFO "")
+
+ message(STATUS "Looking for NVIDIA GPUs present in the system")
+
+ # nvidia-smi-based detection.
+ # Requires the nvidia-smi tool to be installed and available in the path
+ # or in one of the default search locations
+ if (NOT DEFINED GMX_DETECT_GPU_COUNT_NVIDIA_SMI)
+ # try to find the nvidia-smi binary
+ # TODO add location hints
+ find_program(_nvidia_smi "nvidia-smi")
+ if (_nvidia_smi)
+ set(GMX_DETECT_GPU_COUNT_NVIDIA_SMI 0)
+ # execute nvidia-smi -L to get a short list of GPUs available
+ exec_program(${_nvidia_smi_path} ARGS -L
+ OUTPUT_VARIABLE _nvidia_smi_out
+ RETURN_VALUE _nvidia_smi_ret)
+ # process the stdout of nvidia-smi
+ if (_nvidia_smi_ret EQUAL 0)
+ # convert string with newlines to list of strings
+ string(REGEX REPLACE "\n" ";" _nvidia_smi_out "${_nvidia_smi_out}")
+ foreach(_line ${_nvidia_smi_out})
+ if (_line MATCHES "^GPU [0-9]+:")
+ math(EXPR GMX_DETECT_GPU_COUNT_NVIDIA_SMI "${GMX_DETECT_GPU_COUNT_NVIDIA_SMI}+1")
+ # the UUID is not very useful for the user, remove it
+ string(REGEX REPLACE " \\(UUID:.*\\)" "" _gpu_info "${_line}")
+ if (NOT _gpu_info STREQUAL "")
+ list(APPEND GMX_DETECT_GPU_INFO "${_gpu_info}")
+ endif()
+ endif()
+ endforeach()
+
+ check_num_gpu_info(${GMX_DETECT_GPU_COUNT_NVIDIA_SMI} GMX_DETECT_GPU_INFO)
+ set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_NVIDIA_SMI})
+ endif()
+ endif()
+
+ unset(_nvidia_smi CACHE)
+ unset(_nvidia_smi_ret)
+ unset(_nvidia_smi_out)
+ unset(_gpu_name)
+ unset(_line)
+ endif()
+
+ if (UNIX AND NOT (APPLE OR CYGWIN))
+ # /proc/driver/nvidia/gpus/*/information-based detection.
+ # Requires the NVDIA closed source driver to be installed and loaded
+ if (NOT DEFINED GMX_DETECT_GPU_COUNT_PROC AND GMX_DETECT_GPU_COUNT EQUAL 0)
+ set(GMX_DETECT_GPU_COUNT_PROC 0)
+ file(GLOB _proc_nv_gpu_info "/proc/driver/nvidia/gpus/*/information")
+ foreach (_file ${_proc_nv_gpu_info})
+ math(EXPR GMX_DETECT_GPU_COUNT_PROC "${GMX_DETECT_GPU_COUNT_PROC}+1")
+ # assemble information strings similar to the nvidia-smi output
+ # GPU ID = directory name on /proc/driver/nvidia/gpus/
+ string(REGEX REPLACE "/proc/driver/nvidia/gpus.*([0-9]+).*information" "\\1" _gpu_id ${_file})
+ # GPU name
+ file(STRINGS ${_file} _gpu_name LIMIT_COUNT 1 REGEX "^Model:.*" NO_HEX_CONVERSION)
+ string(REGEX REPLACE "^Model:[ \t]*(.*)" "\\1" _gpu_name "${_gpu_name}")
+ if (NOT _gpu_id STREQUAL "" AND NOT _gpu_name STREQUAL "")
+ list(APPEND GMX_DETECT_GPU_INFO "GPU ${_gpu_id}: ${_gpu_name}")
+ endif()
+ endforeach()
+
+ check_num_gpu_info(${GMX_DETECT_GPU_COUNT_PROC} GMX_DETECT_GPU_INFO)
+ set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_PROC})
+
+ unset(_proc_nv_gpu_info)
+ unset(_gpu_name)
+ unset(_gpu_id)
+ unset(_file)
+ endif()
+
+ # lspci-based detection (does not provide GPU information).
+ # Requires lspci and for GPU names to be fetched from the central
+ # PCI ID db if not available locally.
+ if (NOT DEFINED GMX_DETECT_GPU_COUNT_LSPCI AND GMX_DETECT_GPU_COUNT EQUAL 0)
+ set(GMX_DETECT_GPU_COUNT_LSPCI 0)
+ exec_program(lspci ARGS -q
+ OUTPUT_VARIABLE _lspci_out
+ RETURN_VALUE _lspci_ret)
+ # prehaps -q is not supported, try running without
+ if (NOT RETURN_VALUE EQUAL 0)
+ exec_program(lspci
+ OUTPUT_VARIABLE _lspci_out
+ RETURN_VALUE _lspci_ret)
+ endif()
+ if (_lspci_ret EQUAL 0)
+ # convert string with newlines to list of strings
+ STRING(REGEX REPLACE ";" "\\\\;" _lspci_out "${_lspci_out}")
+ string(REGEX REPLACE "\n" ";" _lspci_out "${_lspci_out}")
+ foreach(_line ${_lspci_out})
+ string(TOUPPER "${_line}" _line_upper)
+ if (_line_upper MATCHES ".*VGA.*NVIDIA.*" OR _line_upper MATCHES ".*3D.*NVIDIA.*")
+ math(EXPR GMX_DETECT_GPU_COUNT_LSPCI "${GMX_DETECT_GPU_COUNT_LSPCI}+1")
+ # Try to parse out the device name which should be
+ # included in the lspci -q output between []-s
+ string(REGEX REPLACE ".*\\[(.*)\\].*" "\\1" _gpu_name "${_line}")
+ if (NOT _gpu_name EQUAL "")
+ list(APPEND GMX_DETECT_GPU_INFO "${_gpu_name}")
+ endif()
+ endif()
+ endforeach()
+
+ check_num_gpu_info(${GMX_DETECT_GPU_COUNT_LSPCI} GMX_DETECT_GPU_INFO)
+ set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT_LSPCI})
+ endif()
+
+ unset(_lspci_ret)
+ unset(_lspci_out)
+ unset(_gpu_name)
+ unset(_line)
+ unset(_line_upper)
+ endif()
+ endif()
+
+ if (GMX_DETECT_GPU_COUNT GREATER 0)
+ set(GMX_DETECT_GPU_AVAILABLE YES)
+ else()
+ set(GMX_DETECT_GPU_AVAILABLE NO)
+ endif()
+ set(GMX_DETECT_GPU_AVAILABLE YES CACHE BOOL "Whether any NVIDIA GPU was detected" FORCE)
+
+ set(GMX_DETECT_GPU_COUNT ${GMX_DETECT_GPU_COUNT}
+ CACHE STRING "Number of NVIDIA GPUs detected")
+ set(GMX_DETECT_GPU_INFO ${GMX_DETECT_GPU_INFO}
+ CACHE STRING "basic information on the detected NVIDIA GPUs")
+
+ set(GMX_DETECT_GPU_COUNT_NVIDIA_SMI ${GMX_DETECT_GPU_COUNT_NVIDIA_SMI}
+ CACHE INTERNAL "Number of NVIDIA GPUs detected using nvidia-smi")
+ set(GMX_DETECT_GPU_COUNT_PROC ${GMX_DETECT_GPU_COUNT_PROC}
+ CACHE INTERNAL "Number of NVIDIA GPUs detected in /proc/driver/nvidia/gpus")
+ set(GMX_DETECT_GPU_COUNT_LSPCI ${GMX_DETECT_GPU_COUNT_LSPCI}
+ CACHE INTERNAL "Number of NVIDIA GPUs detected using lspci")
+
+ mark_as_advanced(GMX_DETECT_GPU_AVAILABLE
+ GMX_DETECT_GPU_COUNT
+ GMX_DETECT_GPU_INFO)
+
+ if (GMX_DETECT_GPU_AVAILABLE)
+ message(STATUS "Number of NVIDIA GPUs detected: ${GMX_DETECT_GPU_COUNT} ")
+ else()
+ message(STATUS "Could not detect NVIDIA GPUs")
+ endif()
+
+ endif (NOT DEFINED GMX_DETECT_GPU_COUNT OR NOT DEFINED GMX_DETECT_GPU_INFO)
+endmacro(gmx_detect_gpu)
--- /dev/null
+# If the user did not set GMX_GPU we'll consider this option to be
+# in "auto" mode meaning that we will:
+# - search for CUDA and set GMX_GPU=ON we find it
+# - check whether GPUs are present
+# - if CUDA is not found but GPUs were detected issue a warning
+if (NOT DEFINED GMX_GPU)
+ set(GMX_GPU_AUTO TRUE CACHE INTERNAL "GPU acceleration will be selected automatically")
+endif()
+option(GMX_GPU "Enable GPU acceleration" OFF)
+
+if(GMX_GPU AND GMX_DOUBLE)
+ message(FATAL_ERROR "GPU acceleration is not available in double precision!")
+endif()
+if(GMX_GPU_AUTO AND GMX_DOUBLE)
+ message(WARNING "GPU acceleration is not available in double precision, disabled!")
+ set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
+ set_property(CACHE GMX_GPU_AUTO PROPERTY VALUE OFF)
+endif()
+
+# detect GPUs in the build host machine
+if (GMX_GPU OR GMX_GPU_AUTO AND NOT GMX_GPU_DETECTION_DONE)
+ include(gmxDetectGpu)
+ gmx_detect_gpu()
+endif()
+
+# Depending on the current vale of GMX_GPU and GMX_GPU_AUTO:
+# - OFF, FALSE: Will skip this detection.
+# - OFF, TRUE : Will keep GMX_GPU=OFF if no CUDA is detected, but will assemble
+# a warning message which will be issued at the end of the
+# configuration if GPU(s) were found in the build system.
+# - ON , FALSE: The user requested GPU builds, will require CUDA and will fail
+# if it is not available.
+# - ON , TRUE : Can't happen (GMX_GPU=ON can only be user-set at this point)
+if(GMX_GPU OR GMX_GPU_AUTO AND NOT GMX_GPU_DETECTION_DONE)
+ # We support CUDA >=v3.2 on *nix, but <= v4.1 doesn't work with MSVC
+ if(MSVC)
+ find_package(CUDA 4.1)
+ else()
+ find_package(CUDA 3.2)
+ endif()
+
+ if (EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
+ set(CUDA_FOUND TRUE CACHE INTERNAL "Whether the CUDA toolkit was found" FORCE)
+ else()
+ set(CUDA_FOUND FALSE CACHE INTERNAL "Whether the CUDA toolkit was found" FORCE)
+ endif()
+
+ # assemble warning/error message
+ if (GMX_DETECT_GPU_AVAILABLE)
+ set(_msg "
+ ${GMX_DETECT_GPU_COUNT} NVIDIA GPU(s) found in the system")
+
+ # append GPU names
+ if (NOT GMX_DETECT_GPU_INFO STREQUAL "")
+ set(_msg "${_msg}:")
+ foreach(gpu ${GMX_DETECT_GPU_INFO})
+ set(_msg "${_msg}
+ ${gpu}")
+ endforeach()
+ endif()
+
+ # TODO remove the second part of the message when we'll have compute
+ # capability information from the detection.
+ set(_msg "${_msg}
+ Compute capability information not available, consult the NVIDIA website:
+ https://developer.nvidia.com/cuda-gpus
+ ")
+ endif()
+
+ set(CUDA_NOTFOUND_MESSAGE "
+ mdrun supports native GPU acceleration on NVIDIA hardware with compute
+ capability >=2.0. This requires the NVIDIA CUDA library, which was not
+ found; the location can be hinted by setting CUDA_TOOLKIT_ROOT_DIR as
+ a CMake option (It 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!
+
+ ${_msg}")
+ unset(_msg)
+
+ if (NOT CUDA_FOUND)
+ if (GMX_GPU_AUTO)
+ # Disable GPU acceleration in auto mode
+ message(STATUS "Disabling native GPU acceleration")
+ set_property(CACHE GMX_GPU PROPERTY VALUE OFF)
+ set(CUDA_NOTFOUND_AUTO ON)
+ else ()
+ # the user requested CUDA, but it wasn't found
+ message(FATAL_ERROR "${CUDA_NOTFOUND_MESSAGE}")
+ endif()
+ else()
+ if (GMX_GPU_AUTO)
+ message(STATUS "Enabling native GPU acceleration")
+ set_property(CACHE GMX_GPU PROPERTY VALUE ON)
+ endif()
+ endif() # NOT CUDA_FOUND
+endif()
+# Annoyingly enough, FindCUDA leaves a few variables behind as non-advanced.
+# We need to mark these advanced outside the conditional, otherwise, if the
+# user turns GMX_GPU=OFF after a failed cmake pass, these variables will be
+# left behind in the cache.
+mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_SDK_ROOT_DIR CUDA_VERBOSE_BUILD)
+
+macro(gmx_gpu_setup)
+ # set up nvcc options
+ include(gmxManageNvccConfig)
+
+ # Version info (semicolon used as line separator) for nvcc.
+ get_nvcc_version_info()
+
+ # Check whether we can use atomic operations needed for polling wait for GPU
+ # (to avoid the cudaStreamSynchronize + ECC bug).
+ # With thread-MPI testing atomics has already been carried out, but without
+ # thread-MPI we need to invoke the atomics test independently.
+ if (NOT GMX_THREAD_MPI)
+ set(TEST_TMPI_ATOMICS_ONLY ON CACHE INTERNAL
+ "Test only the atomic operations of thread-MPI.")
+ include(ThreadMPI)
+ endif()
+
+ # no OpenMP is no good!
+ if(NOT GMX_OPENMP)
+ message(WARNING "
+ To use GPU acceleration efficiently, mdrun requires OpenMP multi-threading.
+ With no 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
+ typically inefficient. Note that you need to set both C and C++ compilers that
+ support OpenMP (CC and CXX environment variables, respectively) when using GPUs.")
+ endif()
+endmacro()