-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
Motivation
OpenMP (Open Multi-Processing) is a widely adopted API that provides parallel programming in C, C++, and Fortran, optionally with multi-platform shared-memory support. It is a critical building block for many numerical and scientific libraries, but is used in general-purpose software as well for easy parallelization support.
OpenMP consists of a set of #pragma omp ...
compiler directives that get translated into OpenMP API calls, which are then handled by the runtime library, which is often (but not always) provided with the compiler toolchain and linked dynamically. The translation of OpenMP directives needs to be enabled with a suitable compiler flag, typically -fopenmp
or /openmp
. Passing the same flag to a linker instructs it to link against the OpenMP runtime library found in the compiler toolchain.
Based on the info in recipes alone, there are currently about 50 recipes on CCI currently or upcoming in PRs. Some more notable ones include:
- casadi: a symbolic framework for automatic differentiation and numeric optimization.
- ceres-solver: large-scale, non-linear optimization library.
- colmap: a photogrammetry software for 3D reconstruction from images.
- ensmallen: a header-only C++ library for mathematical optimization.
- fftw: a library for computing the discrete Fourier transform (DFT).
- flann: a library for fast approximate nearest neighbors search in high-dimensional spaces.
- imagemagick: a cross-platform software suite for displaying, converting, and editing raster images.
- g2o: a framework for optimizing graph-based nonlinear error functions.
- lightgbm: a gradient boosting framework that uses tree-based learning algorithms.
- metis: a library for partitioning graphs, finite element meshes, and producing fill-reducing orderings.
- opencv: an open-source computer vision and machine learning software library.
- openmvg: an open-source multiple view geometry library for 3D reconstruction.
- pcl: The Point Cloud Library for 2D/3D image and point cloud processing.
- stdgpu: a library for efficient GPU computing with standard C++.
- suitesparse: a suite of sparse matrix algorithms. The main open-source sparse matrix library.
- vlfeat: an open-source library for computer vision applications.
- xgboost: an optimized gradient boosting library designed for performance and speed.
- xtensor: a C++ library for multi-dimensional arrays with NumPy-like syntax.
Although some of these libraries support alternative parallelization mechanisms, such as plain pthreads or TBB, several of these libraries don't and are entirely (e.g. SuiteSparse) or close to unusable without parallelization via OpenMP enabled.
Problems
- OpenMP runtime libraries are missing from Clang and AppleClang. From both the CI and by default from consumer systems as well.
- Xcode ships without one: https://mac.r-project.org/openmp/
- Not included in major Linux distributions with the
clang
package by default (Ubuntu, Arch, Fedora)
- Using OpenMP as a dependency in recipes needs a lot of extra logic. This is both cumbersome and bug-prone. A typical setup looks like (varying a bit based on whether compiler directives are used in public headers or not):
def requirements(self):
if self.options.with_openmp and self.settings.compiler in ["clang", "apple-clang"]:
self.requires("llvm-openmp/17.0.6", transitive_headers=True, transitive_libs=True)
@property
def _openmp_flags(self):
if self.settings.compiler == "clang":
return ["-fopenmp=libomp"]
elif self.settings.compiler == "apple-clang":
return ["-Xclang", "-fopenmp"]
elif self.settings.compiler == "gcc":
return ["-fopenmp"]
elif self.settings.compiler == "intel-cc":
return ["-Qopenmp"]
elif self.settings.compiler == "sun-cc":
return ["-xopenmp"]
elif is_msvc(self):
return ["-openmp"]
return None
def package_info(self):
...
if self.options.with_openmp:
if self.settings.compiler in ["clang", "apple-clang"]:
self.cpp_info.requires.append("llvm-openmp::llvm-openmp")
openmp_flags = self._openmp_flags
self.cpp_info.cflags = openmp_flags
self.cpp_info.cxxflags = openmp_flags
self.cpp_info.sharedlinkflags = openmp_flags
self.cpp_info.exelinkflags = openmp_flags
- The
llvm-openmp
recipe exports aFindOpenMP.cmake
module with the appropriateOpenMP::OpenMP_CXX
andOpenMP::OpenMP_C
targets, but is otherwise not compatible with the output variables and version set by the officialFindOpenMP.cmake
module.
Here's the current output forfind_package(OpenMP)
withllvm-openmp
:
-- Conan: Target declared 'OpenMP::OpenMP'
OpenMP_FOUND:
OpenMP_VERSION: 17.0.6
OpenMP_C_FOUND:
OpenMP_CXX_FOUND:
OpenMP_CXX_VERSION:
OpenMP_CXX_SPEC_DATE:
OpenMP_CXX_INCLUDE_DIRS:
OpenMP_CXX_LIB_NAMES:
OpenMP_CXX_LIBRARIES:
OpenMP_CXX_FLAGS:
OpenMP_omp_LIBRARY:
Here's the output with the fixes addressing this issue from #22353 applied:
-- Conan: Component target declared 'OpenMP::OpenMP'
-- Conan: Target declared 'llvm-openmp::llvm-openmp'
-- Conan: Including build module from '/home/martin/.conan2/p/b/llvm-e49ba89a6637a/p/lib/cmake/openmp/conan-llvm-openmp-vars.cmake'
-- Found OpenMP: -fopenmp=libomp (found version "5.0")
OpenMP_FOUND: TRUE
OpenMP_VERSION: 5.0
OpenMP_C_FOUND: TRUE
OpenMP_CXX_FOUND: TRUE
OpenMP_CXX_VERSION: 5.0
OpenMP_CXX_SPEC_DATE: 201611
OpenMP_CXX_INCLUDE_DIRS: /home/martin/.conan2/p/b/llvm-e49ba89a6637a/p/include
OpenMP_CXX_LIB_NAMES: omp;m;dl;pthread;rt
OpenMP_CXX_LIBRARIES: llvm-openmp::llvm-openmp
OpenMP_CXX_FLAGS: -fopenmp=libomp
OpenMP_omp_LIBRARY: llvm-openmp::llvm-openmp
Many (typically older) projects rely on these variables being available. The official module also supports C
, CXX
and Fortran
components in the find_package()
call, which the exported module ignores.
Solutions
- Create an
openmp
meta-package - addllvm-openmp
as a requirement for Clang and AppleClang and export the appropriate compiler and linker flags for all others. I have an experimental version available in openmp/system: new recipe #22360. Except for the irrelevant linter errors, it works fine with the leftoverllvm-openmp/18.1.8
from llvm-openmp: match FindOpenMP.cmake output, add MSVC support #22353. There is not much of a precedent for meta-packages on CCI and Conan does not have a suitablepackage_type
for it, but it's something that can hopefully applied for other packages with many alternative implementations in the future as well, such asblas
,lapack
and maybe evenjpeg
. The benefits of having a separateopenmp
package are:
-
The exporting of appropriate build flags without the redundancy and potential bugs.
-
Maps well from the
OpenMP
dependency in CMake when writing a recipe. -
Allows
transitive_headers=True
andtransitive_libs=True
to clearly state the the library uses OpenMP directives in its public headers. -
Allows consumers to swap out or customize the
openmp
package for an alternative runtime or some less common compiler support. -
The
llvm-openmp
package can be pinned to the latest available version for each major version matching the compiler / LLVM version. That would guarantee the stability of ABI and API of the library dependency, to be safe. Realistically, I would expect the latest version to be backwards- and forwards-compatible (up to the available OpenMP standard standard supported by it, of course) thanks to the highly standardized API, plus some non-standard but stable extensions by GOMP, for example. Sincelibomp
and others don't apply SONAME versioning to their library files, ABI compatibility can most likely be assumed as well. I have not looked into the compatibility guarantees any closer than that, though.Alternatively, the C3I Docker images could simply be updated to include
libomp-dev
or the equivalent, but that would not fix the missing library issues for consumers and there's no real blockers to using thellvm-openmp
package (unlike for CUDA, for example), as far as I can tell.
- Fix the
FindOpenMP.cmake
compatibility issues inllvm-openmp
. There are basically two options:
- Disable all compilers except for Clang and AppleClang in
validate()
and create a thin wrapper around CMake'sFindOpenMP.cmake
. This only works for Clang/AppleClang because CMake'sFindOpenMP.cmake
always selects the compiler's native OpenMP runtime to link against and would lead to mixing oflibomp
and the native runtime in transitive dependencies, which is not safe. Note that CMake'sFindOpenMP.cmake
assumes that the runtime library is on the compiler's default search paths, so creating such a wrapper might not be as simple as it looks at first glance. - Add a CMake module that detects the OpenMP standard supported by the compiler preprocessor and exports the necessary CMake variables. This will duplicate CMake's
FindOpenMP
somewhat, but not by a significant amount since it does not need to be nearly as generic. This will work as expected with all compilers - use the compiler's preprocessor for the translation andlibomp
as the runtime. TheFindOpenMP
interface has been very stable as well and can easily be updated if it does changes (as it did in 3.30). This is the approach that was proposed in llvm-openmp: match FindOpenMP.cmake output, add MSVC support #22353.
I'm fine with either approach, but lean towards the latter. Not because I think that other compilers should use libomp
for whatever the reason, but rather because I don't see a reason to limit the use of the package to provide libomp
if the consumer has a specific reason to prefer it and knows what they are doing.
That's all.