1. 開発環境と CMake
この章では、手元の計算機で C++ のプログラムをコンパイルして動かし、MISO をビルドして実行できるようにします。 Ubuntu と Mac で道具を揃え、コンパイルとリンクの仕組み、CMake の使い方、MISO のビルド・テスト・デモの実行、 自分のプログラムから MISO を使う設定までを順に確かめます。 C++ の文法は 第 2 章 から学びます。この章のプログラムは、文法を気にせずそのまま保存して構いません。
必要な道具
この教材は CPU だけで進められます。GPU がなくても、C++ の練習、MISO の配列と並列ループ、MPI による領域分割、 1 次元熱伝導の実習、MISO のデモの実行まで行えます。
道具 |
この教材での役割 |
|---|---|
C++ コンパイラ( |
C++ のソースを機械語に翻訳します。MISO は C++17 を使います |
CMake |
作るもの・使うソース・必要なライブラリを |
MPI(Open MPI) |
複数のプロセスでデータをやり取りするライブラリと、実行コマンド |
yaml-cpp |
設定ファイル |
git |
MISO を取得し、更新を取り込みます |
Python と pymiso |
計算結果を読み込んで図にします。pymiso は MISO に含まれる Python パッケージです |
MPI(Message Passing Interface)は、同じプログラムを複数のプロセスで動かし、プロセスの間でデータを送受信するための規格です。 MISO は計算領域を分けて各プロセスに担当させるために MPI を使います。詳しくは 第 8 章 で説明します。 1 台の計算機の中で複数のプロセスを起動して練習できます。
Ubuntu での準備
Ubuntu 24.04 LTS を基準に説明します。「端末」を開き、次を実行してください。
sudo apt update
sudo apt install build-essential cmake git openmpi-bin libopenmpi-dev \
libyaml-cpp-dev python3 python3-venv
sudo は、管理者の権限でソフトウェアを入れるためのコマンドです。パスワードを入力しても画面には文字が出ません。
共有の計算機でインストールの権限がない場合は、管理者が用意した同等の環境を使います。
パッケージ |
入るもの |
|---|---|
|
C++ コンパイラ GCC( |
|
CMake |
|
git |
|
MPI の実行コマンド( |
|
yaml-cpp のヘッダとライブラリ |
|
Python と、Python の仮想環境(後述)を作る機能 |
入ったことを確かめます。
g++ --version
cmake --version
mpiexec --version
GCC、CMake、Open MPI のバージョンが表示されれば準備完了です。
Mac での準備
Homebrew が対応している macOS を使います。Apple Silicon と Intel Mac では Homebrew の保存先が違うため、
以下では brew --prefix で自分の環境の場所を調べます。
コマンドライン開発ツール
「ターミナル」を開き、次を実行します。画面の案内に沿ってインストールを終えます。 「すでにインストールされています」と表示された場合は、そのまま次へ進めます。
xcode-select --install
これで Apple の C++ コンパイラ(Clang、コマンド名 clang++。c++ や g++ という名前でも呼べます)が入ります。
MISO の CPU 版は、このコンパイラでビルドします。
Homebrew で道具を入れる
Homebrew は、開発用のソフトウェアをまとめて入れ・更新する道具です。brew --version が動かない場合は、
Homebrew 公式サイト に掲載されているインストールコマンドを実行し、
終了時に表示される Next steps のコマンドも実行してください(ターミナルから brew を見つけられるようにする設定です)。
その後、次を実行します。
brew install git cmake open-mpi yaml-cpp python
入ったことを確かめます。
clang++ --version
cmake --version
mpiexec --version
brew --prefix yaml-cpp
Homebrew で入れたライブラリは brew --prefix が表示する場所(Apple Silicon では通常 /opt/homebrew)の下にあります。
CMake は、PATH に入っている Homebrew の bin から、この場所も探します。
MISO を取得する
作業用のフォルダを決め、MISO を取得します。以下では $HOME/miso-work を使いますが、別の場所でも構いません。
mkdir -p "$HOME/miso-work"
cd "$HOME/miso-work"
git clone https://github.com/is-isee/MISO.git
これで $HOME/miso-work/MISO ができます。この教材では、MISO を置いたフォルダを /path/to/MISO と書きます。
コマンドを入力するときは、自分の保存先の絶対パス(/ で始まるパス。この例では $HOME/miso-work/MISO)に置き換えてください。
MISO のフォルダの主な中身は次のとおりです。
場所 |
中身 |
|---|---|
|
MISO 本体のヘッダファイル( |
|
MISO 本体と、そのユニットテストのビルド設定 |
|
ユニットテスト( |
|
デモ問題。 |
|
計算結果を読む Python パッケージ |
|
開発のルールと手順 |
コンパイルとリンク
コンパイラで実行ファイルを作る
Fortran と同じく、C++ のソースはコンパイラで実行ファイルに変換してから動かします。
作業用のフォルダを作り、次の内容を hello.cpp という名前で保存してください。
mkdir -p "$HOME/miso-work/hello"
cd "$HOME/miso-work/hello"
#include <cmath>
#include <iostream>
int main() {
const double x = 2.0;
std::cout << "sqrt(" << x << ") = " << std::sqrt(x) << '\n';
return 0;
}
main はプログラムの入口となる関数で、Fortran の program に当たります。
コンパイルして実行します(Mac では g++ を clang++ に置き換えます)。
g++ -std=c++17 -O2 hello.cpp -o hello
./hello
sqrt(2) = 1.41421
-std=c++17 は C++17 の規格で翻訳する指定、-O2 は最適化の強さ、-o hello は実行ファイルの名前です。
コンパイルとリンクの 2 段階
上のコマンドは、実際には 2 つの段階をまとめて行っています。
コンパイル:
.cppを 1 つずつ翻訳し、オブジェクトファイル(.o)を作ります。リンク:オブジェクトファイルと、使っているライブラリの中身を結び付け、実行ファイルを作ります。
段階を分けて実行することもできます。-c は「コンパイルだけを行う」指定です。
g++ -std=c++17 -O2 -c hello.cpp -o hello.o # compile: produces hello.o
g++ hello.o -o hello # link: produces hello
ここでの「オブジェクトファイル」はコンパイル結果のファイルを指す用語で、クラスの実体(オブジェクト)とは別の意味です。
ヘッダと #include
#include <cmath> は、cmath というヘッダファイルの中身をその場所に読み込む指示です。
ヘッダには、関数や型の宣言(名前と引数の型など、使い方の情報)が書かれています。
コンパイラは宣言を見て、std::sqrt(x) の呼び出しが正しいかを確かめます。
Fortran で use したモジュールの情報を見て引数を確かめるのに近い役割です。
ヘッダは決まったフォルダから探されます。標準ライブラリのヘッダはコンパイラが場所を知っていますが、
それ以外のヘッダは -I フォルダ で探す場所を足します。たとえば MISO のヘッダは #include <miso/core.hpp> のように書くので、
/path/to/MISO/miso/include を -I で渡すと miso/core.hpp が見つかります。
宣言だけでは、関数の実体(計算の中身)はありません。実体がライブラリの中にある場合は、リンクの段階でそのライブラリを指定します。
yaml-cpp を使う短いプログラムで確かめます。次を yaml_check.cpp として保存してください。
#include <iostream>
#include <yaml-cpp/yaml.h>
int main() {
const YAML::Node node = YAML::Load("grid: {i_size: 128}");
std::cout << "i_size = " << node["grid"]["i_size"].as<int>() << '\n';
return 0;
}
-lyaml-cpp は「リンクのときに libyaml-cpp というライブラリを使う」指定です。
g++ -std=c++17 yaml_check.cpp -o yaml_check -lyaml-cpp
./yaml_check
i_size = 128
Mac では Homebrew の場所を -I と -L(ライブラリを探すフォルダ)で教えます。
clang++ -std=c++17 yaml_check.cpp -o yaml_check \
-I"$(brew --prefix)/include" -L"$(brew --prefix)/lib" -lyaml-cpp
MISO はヘッダオンリーのライブラリです。計算の部品はすべてヘッダ(miso/include/miso/*.hpp)に実体ごと書かれていて、
MISO 自身を前もってライブラリにコンパイルしておく必要はありません。
ただし、MISO が使う MPI と yaml-cpp は、ヘッダの場所とリンクするライブラリの指定が必要です。
MPI については、Open MPI が用意する mpicxx というコマンドが、MPI のヘッダとライブラリの指定を足して g++(Mac では clang++)を呼びます。
mpicxx --showme で、足される指定を表示できます(Open MPI の場合)。
練習 1-1
yaml_check.cpp を、-lyaml-cpp を付けずにビルドしてください。
エラーがコンパイルとリンクのどちらの段階で起きたかを、表示から判断して説明してください。
解答・解説
g++ -std=c++17 yaml_check.cpp -o yaml_check
Ubuntu では次のような表示が出ます(一部を抜粋)。
yaml_check.cpp:(.text+0x34): undefined reference to `YAML::Load(char const*)'
undefined reference(未定義の参照)はリンクの段階のエラーです。
#include <yaml-cpp/yaml.h> で宣言は見えているので、コンパイルは通ります。
リンクのときに YAML::Load の実体を持つライブラリが指定されていないため、実行ファイルを作れません。
Mac の Clang では Undefined symbols for architecture ... と表示されます。これもリンクのエラーです。
一方、ヘッダが見つからないときは fatal error: yaml-cpp/yaml.h: No such file or directory のように表示されます。
これはコンパイルの段階のエラーで、-I の指定かパッケージのインストールを確かめます。
CMake の仕組み
設定・ビルド・実行の 3 段階
ソースやライブラリが増えると、コンパイルとリンクのコマンドを手で書くのは大変です。
Fortran でも使われる make は、Makefile に書かれた手順に従ってビルドします。
CMake は、作るもの・使うソース・必要なライブラリを CMakeLists.txt に書くと、その計算機に合った Makefile などを生成します。
CMake はビルドの手順を組み立てる道具で、C++ の翻訳そのものは GCC や Clang が行います。
段階 |
コマンドの形 |
行うこと |
|---|---|---|
設定・生成 |
|
|
ビルド |
|
生成した手順でコンパイルとリンクを行います |
実行 |
|
できた実行ファイルを動かします |
-S . は「ソース(CMakeLists.txt)は今いるフォルダにある」、-B build は「生成物は build/ に置く」という指定です。
生成物をソースと別のフォルダに置くので、build/ を消せばいつでもやり直せます。
設定の結果は build/CMakeCache.txt に記録され、次のビルドでも使われます。
最小の CMakeLists.txt
hello.cpp と yaml_check.cpp を保存したフォルダに、次の CMakeLists.txt を保存します。
cmake_minimum_required(VERSION 3.18)
project(hello LANGUAGES CXX)
find_package(yaml-cpp REQUIRED)
add_executable(hello hello.cpp)
target_compile_features(hello PRIVATE cxx_std_17)
add_executable(yaml_check yaml_check.cpp)
target_compile_features(yaml_check PRIVATE cxx_std_17)
target_link_libraries(yaml_check PRIVATE yaml-cpp::yaml-cpp)
CMakeLists.txt は CMake 用の言語で、命令(引数 ...) の形で書きます。
命令 |
役割 |
|---|---|
|
必要な CMake の最低バージョンを指定します |
|
プロジェクトに名前を付け、C++ のコンパイラを使うことを伝えます |
|
yaml-cpp を探し、その使い方(ヘッダの場所、ライブラリ)を取り込みます。 |
|
|
|
このターゲットに C++17 以上が必要だと伝えます |
|
|
ターゲット(target)は、実行ファイルやライブラリなど、ビルドで作るものに付ける名前です。
ソースや設定をターゲットごとにまとめるので、1 つの CMakeLists.txt で複数のプログラムを区別して作れます。
yaml-cpp::yaml-cpp は find_package が用意するターゲットで、ライブラリの場所だけでなく、ヘッダの場所などの情報も持っています。
このため target_link_libraries は、リンクの指定に加えて、コンパイルに必要な -I などもまとめて伝えます。
手で -I や -l を並べる必要がなくなるのが、CMake を使う大きな利点です。
PRIVATE は、その設定をこのターゲット自身だけに使う指定です。
ライブラリ側のターゲットで PUBLIC や INTERFACE を使うと、そのライブラリを使う側のターゲットにも設定が伝わります(MISO はこの仕組みを使います)。
C++ のクラスの private・public(第 4 章)とは別のものです。
設定・ビルド・実行します。
cmake -S . -B build
cmake --build build --parallel 4
./build/hello
./build/yaml_check
--parallel 4 はコンパイルを最大 4 つ同時に進める指定で、計算を実行するときのプロセス数とは関係ありません。
cmake --build build --verbose とすると、CMake が実際に呼んだコンパイラのコマンドが表示されます。
ビルド種別
ビルド種別(build type)は、最適化やデバッグ情報の付け方をまとめて選ぶ設定です。設定の段階で -D を使って渡します。
cmake -S . -B build_debug -DCMAKE_BUILD_TYPE=Debug
種別 |
GCC・Clang に渡る主な指定 |
使う場面 |
|---|---|---|
|
|
計算の実行。速さを優先します |
|
|
不具合を調べるとき。最適化をせず、デバッガで変数を見られるようにします |
-D名前=値 は、CMake の設定項目(キャッシュ変数)に値を渡す書き方です。
ビルド種別を何も指定しないと、上の hello のような CMake のプロジェクトは最適化の指定なしでビルドされます。
MISO とデモの CMakeLists.txt は、指定がなければ Release を選ぶように書かれています。
-DNDEBUG が付くと、C++ の assert(条件が成り立たなければ止める確認)は取り除かれます。
MISO の既定は Release なので、assert による確認は既定のビルドでは働きません。
種別やコンパイラを変えるときは、build_debug のように別のビルドフォルダを使います。
同じフォルダで設定を変えると、前の設定が CMakeCache.txt に残って混ざることがあります。
ソースを変更したときのビルド
ソースを編集したら、cmake --build build をもう一度実行します。
変更されたファイルと、それを #include しているファイルだけが翻訳し直されます。
CMakeLists.txt を変更した場合も、次の cmake --build のときに CMake が自動で設定をやり直します。
生成された build/Makefile を手で直すことはありません。
MISO のビルドとユニットテスト
miso/CMakeLists.txt の中身
MISO 本体のビルド設定は miso/CMakeLists.txt にあります。主な部分を抜粋します(説明のため一部を省略しています)。
# CUDA
option(MISO_USE_CUDA "Enable CUDA support" OFF)
# MISO as a header-only library
add_library(MISO INTERFACE)
add_library(MISO::MISO ALIAS MISO)
target_include_directories(MISO INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
# MPI
find_package(MPI REQUIRED)
target_link_libraries(MISO INTERFACE MPI::MPI_CXX)
# yaml-cpp
find_package(yaml-cpp QUIET)
if(NOT yaml-cpp_FOUND)
message(STATUS "yaml-cpp not found; fetching with FetchContent")
include(FetchContent)
FetchContent_Declare(
yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG 0.8.0)
FetchContent_MakeAvailable(yaml-cpp)
endif(NOT yaml-cpp_FOUND)
target_link_libraries(MISO INTERFACE yaml-cpp::yaml-cpp)
# Unit tests
option(ENABLE_TESTING "Enable Testing" ON)
if(ENABLE_TESTING)
enable_testing()
add_subdirectory(tests)
endif(ENABLE_TESTING)
設定 |
意味 |
|---|---|
|
中身をコンパイルしないライブラリのターゲット |
|
|
|
|
|
|
|
計算機に yaml-cpp があればそれを使い、なければ GitHub から取得して一緒にビルドします |
|
GPU(CUDA)を使うかどうかの切り替えです。既定は使いません |
|
ユニットテストもビルドするかどうかの切り替えです。既定はビルドします |
INTERFACE は「MISO::MISO を使う側のターゲットに伝える」という指定です。
このため、target_link_libraries(自分のプログラム PRIVATE MISO::MISO) と 1 行書くだけで、
MISO のヘッダの場所、MPI、yaml-cpp の指定がまとめて自分のプログラムに伝わります。
FetchContent は、必要なソースを CMake の設定の段階でインターネットから取得する仕組みです。 yaml-cpp を入れていない場合や、ユニットテスト用の doctest(テストと計算結果の比較 で説明します)を取得する場合に使われるので、 最初の設定にはインターネットへの接続が必要です。
ビルドとテストの実行
ビルドフォルダは MISO のフォルダの外に作っても構いません。ここでは $HOME/miso-work/build_miso を使います。
cd "$HOME/miso-work"
cmake -S /path/to/MISO/miso -B build_miso -DMISO_USE_CUDA=OFF
cmake --build build_miso --parallel 4
-S には miso/CMakeLists.txt のあるフォルダ(/path/to/MISO/miso)を渡します。
テストの実行ファイルがいくつも作られ、数分かかることがあります。
ビルドが終わったら、ユニットテストを実行します。ユニットテストは、配列や並列ループなどの部品ごとに、期待どおりに動くかを確かめるプログラムです。 ctest は、CMake に登録されたテストをまとめて実行する道具です。
cd build_miso
OMPI_MCA_btl_vader_single_copy_mechanism=none \
OMPI_MCA_rmaps_base_oversubscribe=yes \
ctest --output-on-failure
最後に次のように表示されれば成功です(テストの数は MISO の版によって変わります)。
100% tests passed, 0 tests failed out of 15
先頭の 2 行は、このコマンドの間だけ Open MPI の動作を変える環境変数です。 テストの一部は MPI の 2 プロセスで動くため、CPU のコア数が少ない計算機やコンテナでも実行できるようにしています。 意味と、テストの読み方、テストを絞って実行する方法は テストと計算結果の比較 にまとめています。
デモの実行と出力の読み込み
デモをビルドして実行する
MISO の demo/ には、問題ごとのデモがあります。2 次元の Orszag–Tang 渦(demo/mhd2d_vortex)を動かします。
デモの CMakeLists.txt は、MISO 本体(../../miso)を取り込んで src/main.cpp から実行ファイルを作ります。
cd /path/to/MISO/demo/mhd2d_vortex
cmake -S . -B build -DUSE_CUDA=OFF
cmake --build build --parallel 4
./app_run.sh
デモでは GPU の切り替えを USE_CUDA という名前で渡します(MISO 本体の MISO_USE_CUDA とは名前が違います)。
app_run.sh は、Open MPI 用の環境変数を demo/shared/openmpi_config.env から読み込み、2 プロセスで計算を実行するスクリプトです。
実行するコマンドを表示してから計算を始めます。
+ mpiexec -np 2 /path/to/MISO/demo/mhd2d_vortex/build/mhd2d_vortex --config=/path/to/MISO/demo/mhd2d_vortex/config.yaml
time = 0.00; n_step = 0; n_output = 0
time = 0.01; n_step = 4; n_output = 1
...
time = 0.80; n_step = 354; n_output = 80
計算の条件は demo/mhd2d_vortex/config.yaml に書かれています。格子点数、終了時刻(time.tend)、出力の間隔(time.dt_output)、
MPI での分割数(mpi.x_procs など)を変えられます。
出力されるファイル
結果は config.yaml と同じフォルダの data/ に書かれます(config.yaml の io.save_dir で変えられます)。
ファイル |
中身 |
|---|---|
|
既定値を補った、実際に使った設定 |
|
全体の格子の座標 |
|
各プロセス(ランク)が担当する領域の位置 |
|
各出力の時刻、出力番号、ステップ数 |
|
各プロセスが担当する領域の MHD の変数(密度、速度、磁場など 9 つ) |
mhd.*.bin はプロセスごとに分かれたバイナリファイルです。
config.yaml の io.continue: true により、data/ に結果があると、そこから計算を続けます。
最初からやり直すときは ./app_clean.sh で build/、data/、figs/ を消してから、ビルドと実行をやり直します。
pymiso で読み込む
pymiso は、プロセスごとのファイルをつなげて全体の配列として読み込む Python パッケージです。 Python のパッケージは、MISO 用の仮想環境(その中に入れたパッケージだけを使う、独立した Python の環境)に入れます。
python3 -m venv "$HOME/miso-work/venv"
source "$HOME/miso-work/venv/bin/activate"
cd /path/to/MISO
pip install -e ".[viz]"
source .../activate は、このターミナルで仮想環境の Python を使うようにするコマンドです。
新しいターミナルを開いたら、もう一度実行します。
pip install -e ".[viz]" は、MISO のフォルダにある pymiso と、図を描くための matplotlib などを入れます。
デモには図を描くスクリプト plot_data.py があります。
cd /path/to/MISO/demo/mhd2d_vortex
python plot_data.py
figs/ に、各時刻の密度の図(py_00000000.png など)が保存されます。
plot_data.py の中では、次のように結果を読み込んでいます(demo/mhd2d_vortex/plot_data.py から抜粋。... は省略した部分です)。
d = pymiso.Data(data_dir=this_dir / "data")
...
d.load(n)
ax.pcolormesh(d.x, d.y, d.ro.T, shading="auto", vmin=0.2, vmax=2.5, cmap="inferno")
pymiso.Data に data/ の場所を渡し、d.load(n) で出力番号 n の結果を読みます。
d.ro が密度、d.x・d.y が座標の NumPy 配列です。
ほかのデモ、設定の変え方、GPU 版の実行方法は、MISO_docs の Quick Start にまとめられています。
自分のプログラムから MISO を使う
第 7 章と第 10 章の実習では、MISO のフォルダの外に自分のフォルダを作り、MISO の部品を使うプログラムを書きます。 そのための最小の CMake の設定を作り、MISO が使えることを確かめます。
作るもの
MISO のフォルダの外に、作業用のフォルダを作ります。
mkdir -p "$HOME/miso-work/miso_check"
cd "$HOME/miso-work/miso_check"
このフォルダに、確認用のプログラム main.cpp と CMakeLists.txt を保存します。
main.cpp は、MPI を始め、MISO の配列に 0〜99 を入れて合計を求めるプログラムです。
miso::Array1D、miso::for_each、miso::reduce、MISO_LAMBDA は 第 7 章 で説明します。ここではそのまま保存してください。
#include <iostream>
#include <miso/core.hpp>
int main(int argc, char **argv) {
miso::Env env(argc, argv); // starts MPI, and cleans up at the end
const int n = 100;
miso::Array1D<float, miso::backend::Host> a(n);
auto av = a.view();
miso::for_each(miso::backend::Host{}, miso::Range1D{0, n},
MISO_LAMBDA(int i) { av[i] = float(i); });
const float sum = miso::reduce(
miso::backend::Host{}, miso::Range1D{0, n}, 0.0f,
MISO_LAMBDA(int i) { return av[i]; },
MISO_LAMBDA(float x, float y) { return x + y; });
if (miso::mpi::is_root()) {
std::cout << "MPI processes: " << miso::mpi::size() << '\n'
<< "sum of 0..99: " << sum << " (expected 4950)\n";
}
return 0;
}
#include <miso/core.hpp> は、配列・並列ループ・MPI の環境など、MISO の共通の部品をまとめて読み込むヘッダ(miso/include/miso/core.hpp)です。
CMakeLists.txt
cmake_minimum_required(VERSION 3.18)
project(miso_check LANGUAGES CXX)
set(MISO_DIR "" CACHE PATH "Root of the MISO repository")
if(NOT EXISTS "${MISO_DIR}/miso/CMakeLists.txt")
message(FATAL_ERROR "Set -DMISO_DIR=/path/to/MISO")
endif()
set(ENABLE_TESTING OFF CACHE BOOL "Build the unit tests of MISO")
add_subdirectory("${MISO_DIR}/miso" miso)
add_executable(miso_check main.cpp)
target_compile_features(miso_check PRIVATE cxx_std_17)
target_link_libraries(miso_check PRIVATE MISO::MISO)
設定 |
役割 |
|---|---|
|
MISO を置いた場所を入れる設定項目 |
|
|
|
MISO のユニットテストをビルドしないようにします。 |
|
MISO の |
|
|
|
MISO のヘッダの場所、MPI、yaml-cpp をまとめて |
add_subdirectory は、別のフォルダの CMakeLists.txt を、自分のプロジェクトの一部として読み込む命令です。
MISO のフォルダがプロジェクトの外にあるときは、2 つ目の引数(生成物の置き場)が必要です。
ENABLE_TESTING を OFF にするのは、自分のプログラムだけを速くビルドするためです。
MISO のユニットテストは、前の節 のように MISO 単独のビルドで実行します。
option(ENABLE_TESTING ...) は、同じ名前の設定項目がすでにあればその値を使うので、先に OFF を入れておくとテストが外れます。
ビルド種別を指定しなければ、MISO の CMakeLists.txt が Release を選びます。
ビルドと実行
/path/to/MISO を、MISO を置いた場所の絶対パスに置き換えて設定します。
cmake -S . -B build -DMISO_DIR=/path/to/MISO
cmake --build build --parallel 4
mpiexec -np 2 ./build/miso_check
mpiexec -np 2 は、同じプログラムを 2 つのプロセスで起動するコマンドです。次の表示が出れば準備完了です。
MPI processes: 2
sum of 0..99: 4950 (expected 4950)
どちらのプロセスも同じ計算をしますが、表示は 1 つのプロセス(ランク 0)だけが行います。
MISO のデモの CMakeLists.txt は、add_subdirectory の代わりに FetchContent_Declare(MISO SOURCE_DIR ...) と FetchContent_MakeAvailable(MISO) で MISO を取り込んでいます。
手元のフォルダを指定する使い方では、どちらも miso/CMakeLists.txt を読み込んで MISO::MISO を作る点は同じです。
練習 1-2
-DMISO_DIR=... を付けずに、新しいビルドフォルダ build_nodir で設定してください。
表示されたメッセージを確かめ、設定・ビルド・実行のどの段階で止まったかと、その理由を CMakeLists.txt の行と対応づけて説明してください。
解答・解説
cmake -S . -B build_nodir
CMake Error at CMakeLists.txt:6 (message):
Set -DMISO_DIR=/path/to/MISO
設定の段階で止まります。MISO_DIR の値は空のままなので、"${MISO_DIR}/miso/CMakeLists.txt" は /miso/CMakeLists.txt になり、
if(NOT EXISTS ...) の条件が成り立ちます。その中の message(FATAL_ERROR ...) が説明を表示して設定を止めます。
この確認がないと、add_subdirectory が「フォルダがない」という分かりにくいエラーを出します。
確かめたら、build_nodir は消して構いません。
練習 1-3
miso_check を Debug 種別で、build_debug という別のビルドフォルダに作ってください。
cmake -L -N build_debug で設定項目の一覧を表示し、CMAKE_BUILD_TYPE、MISO_DIR、ENABLE_TESTING、MISO_USE_CUDA の値を確かめてください。
解答・解説
cmake -S . -B build_debug -DMISO_DIR=/path/to/MISO -DCMAKE_BUILD_TYPE=Debug
cmake --build build_debug --parallel 4
cmake -L -N build_debug | grep -E "BUILD_TYPE|MISO|ENABLE_TESTING"
CMAKE_BUILD_TYPE:STRING=Debug
ENABLE_TESTING:BOOL=OFF
MISO_DIR:PATH=/path/to/MISO
MISO_USE_CUDA:BOOL=OFF
-L は設定項目の一覧を表示し、-N は設定をやり直さずに CMakeCache.txt を読むだけにする指定です。
MISO_USE_CUDA は MISO 側の option が既定値 OFF で作った項目です。
cmake --build build_debug --verbose で、コンパイルのコマンドに -g が付き、-O3 が付いていないことも確かめられます。
build(Release)と build_debug(Debug)はフォルダが別なので、互いの設定は混ざりません。
手でコンパイルする場合との対応
CMake が行っていることを、手で書いたコマンドと比べておきます。Ubuntu では次のコマンドでも miso_check を作れます。
mpicxx -std=c++17 -O2 -I/path/to/MISO/miso/include main.cpp -o miso_check_manual -lyaml-cpp
mpicxx が MPI の指定を足し、-I が MISO のヘッダの場所、-lyaml-cpp が yaml-cpp を指定します。
MISO::MISO をリンクすると、これらを CMake がまとめて指定します。
GPU 用のビルドや計算機ごとの違いがあっても CMakeLists.txt を変えずに済むので、この教材では CMake を使います。
うまくいかないとき
最初のエラーから順に確かめます。設定が成功してからビルドし、ビルドが成功してから実行します。
表示・症状 |
確かめること |
|---|---|
|
Ubuntu では |
|
yaml-cpp が見つからず、インターネットから取得しようとしています。yaml-cpp を入れる(Ubuntu は |
ユニットテストの設定で doctest の取得に失敗する |
doctest はいつもインターネットから取得します。接続を確かめます |
|
|
|
手でコンパイルするときは |
|
yaml-cpp がリンクされていません。手でコンパイルするときは |
|
CPU のコア数より多いプロセスを起動しようとしています。動作確認だけなら |
|
|
コンパイラを変えたのに前のコンパイラが使われる |
前の設定が |
GPU を使う段階になったら
MISO は NVIDIA の GPU で計算する CUDA 版も作れます。
MISO 本体では -DMISO_USE_CUDA=ON、デモでは -DUSE_CUDA=ON を設定のときに渡します。
CUDA の開発環境(CUDA Toolkit と対応するドライバ)が必要で、Mac の GPU では動きません。
GPU 用のビルドと、自分のプログラムを GPU で動かす設定は 第 9 章 で説明します。
次に読むところ
準備ができたら、第 2 章:変数・関数・制御文 へ進みます。
この章で作った miso_check のフォルダと CMakeLists.txt の形は、第 7 章 と 第 10 章 の実習で使います。