👐

CMake + Apple Clang で OpenMP 5 を使う

2024/03/31に公開1

Mac で OpenMP 5 を使おうとして、苦戦したのでメモ。
C++ は割とわかっていないことが多いので、何か間違っていたらコメントください!

前提

  • ビルドには CMake 3.29.0 を利用
    • OpenMP は以下のように find_package() を使って発見します。
CMakeLists.txt
find_package(OpenMP 5.0)
if(OpenMP_CXX_FOUND)
  target_link_libraries(${PROJECT_NAME} PUBLIC OpenMP::OpenMP_CXX)
else()
  message(WARNING "OpenMP not found. It runs on single thread.")
endif()
  • MacBook Pro 2019, Intel Core i9
  • macOS Sonoma 14.2.1

試したこと

  1. gcc の OpenMP を使う
  2. Apple Clang + libomp

結論からいえば、2がうまく行きましが、設定に一手間必要でした。
1の方は gcc(brew で入れたもの) の OpenMP がどうしても 4.5 までしかいかず、 5 系は使えなかった・・・
gcc のドキュメントを見ると、OpenMP 5.0 の実装ステータスが N のものが結構あるので、まだだめなのかもしれません。

ここからは 2 の方の設定方法を書いていきます。

libomp

libomp は LLVM の OpenMP ランタイムライブラリです。 brew 経由でインストールできます。

brew install libomp

たぶん、libomp でうまくいくということは LLVM の Clang を使えばきっとうまくいくんですが、 VSCode の CMake 拡張機能で「構成」タブに LLVM が現れなかったので Apple Clang に libomp を組み合わせる方針でいきました。


Apple Clang は謎に2つある

CMake に libomp の場所を教える

CMake を使わずにコンパイルする際には -Xpreprocessor -fopenmp -lomp オプションを付ければいけるようです(参考:Apple ClangでOpenMPを使う|Qiita)。しかし、 CMake でやろうとすると、 OpenMP が見つからないというエラーが出てなかなかうまく行きませんでした。

-- Could NOT find OpenMP_C (missing: OpenMP_C_FLAGS OpenMP_C_LIB_NAMES) (Required is at least version "5.0")
-- Could NOT find OpenMP_CXX (missing: OpenMP_CXX_FLAGS OpenMP_CXX_LIB_NAMES) (Required is at least version "5.0")

色々探し回った結果、みつけたのがこちら。

https://gist.github.com/scivision/16c2ca1dc250f54d34f1a1a35596f4a0

For macOS, first do:
brew install libomp
and set in ~/.zshrc
export OpenMP_ROOT=$(brew --prefix)/opt/libomp

環境変数 OpenMP_ROOT を設定するのが肝でした。記事の通り ~/.zshrc に以下の記述を加えます。

export OpenMP_ROOT=$(brew --prefix)/opt/libomp

これで、 CMake が libomp の場所を知ることができます!

場合によってはもうひと手間要る

この状態で cmake コマンドを実行すると、以下の警告が出ます。

CMake Warning (dev) at CMakeLists.txt:29 (find_package):
  Policy CMP0074 is not set: find_package uses <PackageName>_ROOT variables.
  Run "cmake --help-policy CMP0074" for policy details.  Use the cmake_policy
  command to set the policy and suppress this warning.

  Environment variable OpenMP_ROOT is set to:

    /usr/local/opt/libomp

  For compatibility, CMake is ignoring the variable.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Could NOT find OpenMP_C (missing: OpenMP_C_FLAGS OpenMP_C_LIB_NAMES) (Required is at least version "5.0")
-- Could NOT find OpenMP_CXX (missing: OpenMP_CXX_FLAGS OpenMP_CXX_LIB_NAMES) (Required is at least version "5.0")

要約すると、CMP0074 というポリシーが設定されておらず、環境変数の設定が無視されているようです。 CMP0074 を見てみましょう。

❯ cmake --help-policy CMP0074
CMP0074
-------

.. versionadded:: 3.12

``find_package()`` uses ``<PackageName>_ROOT`` variables.

In CMake 3.12 and above the ``find_package(<PackageName>)`` command now
searches prefixes specified by the ``<PackageName>_ROOT`` CMake
variable and the ``<PackageName>_ROOT`` environment variable.
Package roots are maintained as a stack so nested calls to all ``find_*``
commands inside find modules and config packages also search the roots as
prefixes.  This policy provides compatibility with projects that have not been updated to avoid using ``<PackageName>_ROOT`` variables for other purposes.

The ``OLD`` behavior for this policy is to ignore ``<PackageName>_ROOT``
variables.  The ``NEW`` behavior for this policy is to use
``<PackageName>_ROOT`` variables.

This policy was introduced in CMake version 3.12.
It may be set by ``cmake_policy()`` or ``cmake_minimum_required()``.
If it is not set, CMake warns, and uses ``OLD`` behavior.

.. note::
  The ``OLD`` behavior of a policy is
  ``deprecated by definition``
  and may be removed in a future version of CMake.

このポリシーは CMake 3.12 で追加されたポリシーで、 find_package()<PackageName>_ROOT を使う、というポリシーになっています。
このポリシーを NEW に設定するとポリシーが採用され、CMake または環境の変数<PackageName>_ROOT によってパッケージルートを指定することが可能になります。
<PackageName>_ROOT を他の目的で使っているプロジェクトとの互換性を保つため、デフォルトでは OLD になっているようです。

以下のように明示的にポリシーを採用してあげるか、

CMakeLists.txt
cmake_policy(SET CMP0074 NEW)

cmake_minumum_required を 3.12 以上に上げれば OpenMP_ROOT が使われるようになります。

CMakeLists.txt
cmake_minimum_required(VERSION 3.12)

再度 cmake コマンドを実行すると・・・

❯ cmake -S . -B build        
-- Found OpenMP_C: -Xclang -fopenmp (found suitable version "5.0", minimum required is "5.0")
-- Found OpenMP_CXX: -Xclang -fopenmp (found suitable version "5.0", minimum required is "5.0")
-- Found OpenMP: TRUE (found suitable version "5.0", minimum required is "5.0")

行けた!!

おわりに

というわけで、CMake + Apple Clang で OpenMP 5 を使うはなしでした。
他の方法があれば、ぜひコメントにて教えて下さい!

Discussion