openmp について
openmpは、C、C++、FortranでOpenMPの共有メモリ並列処理を扱うエージェント向けスキルです。CPUループ、タスク、GPUオフロード、調整、プロファイリング、正確性やスケーリングの問題を解説します。
主な機能
- 01ループ並列化とスケジューリング
- 02リダクションとデータ共有属性
- 03SIMDとタスク並列処理
- 04OpenMPによるGPUオフロード
- 05スレッド調整とプロファイリング
- 06競合と偽共有の診断
活用シーン
- 01マルチコアCPUのループ並列化
- 02不規則な処理をタスクで並列化
- 03OpenMP target指示句によるGPUオフロード
- 04スケーリングやスレッド設定の問題診断
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill openmpインストール後、Claude Code で/openmpを入力して読み込まれたか確認します。
詳細
OpenMP
目的
スケジューリング句を指定した #pragma omp parallel for、リダクション、データ共有属性、SIMD ヒント、タスク並列処理、OpenMP 5.x の GPU target オフロード、よくある落とし穴(フォルスシェアリング、データ競合)、環境変数によるチューニング、Score-P または TAU を使ったプロファイリングを通じて、OpenMP の共有メモリー並列処理をエージェントに案内します。
使用する場面
- マルチコア CPU で C/C++/Fortran のループを並列化する
- リダクション(合計、最大値、カスタム)を実装する
- 不規則なワークロードにタスク並列処理を使用する
- OpenMP の target ディレクティブで計算を GPU にオフロードする
- スケーリングの問題(フォルスシェアリング、負荷不均衡)を診断する
- スレッド数とスピン動作を調整する
ワークフロー
1. 基本的な parallel for
#include <omp.h>
#include <stdio.h>
int main(void) {
const int n = 1000000;
double sum = 0.0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < n; i++)
sum += i * 0.001;
printf("sum = %f, threads = %d\n", sum, omp_get_max_threads());
return 0;
}
gcc -fopenmp -O3 -o omp_sum omp_sum.c
export OMP_NUM_THREADS=8
./omp_sum
2. schedule 句
#pragma omp parallel for schedule(static) // equal chunks, low overhead
#pragma omp parallel for schedule(dynamic, 64) // dynamic chunks of 64
#pragma omp parallel for schedule(guided) // decreasing chunk size
#pragma omp parallel for schedule(auto) // compiler/runtime decides
| スケジュール | 適した用途 |
|---|---|
static | 各反復の作業量が均一 |
dynamic | 反復ごとのコストが変動 |
guided | 反復ごとのコストが減少 |
static,1 | インターリーブされたチャンクによるキャッシュブロッキング |
3. データ共有属性
int shared_var = 0;
#pragma omp parallel private(i) shared(shared_var)
{
int i = omp_get_thread_num();
#pragma omp atomic
shared_var += i;
}
// firstprivate — copy in; lastprivate — copy out after loop
#pragma omp parallel for firstprivate(offset) lastprivate(result)
for (int i = 0; i < n; i++) { ... }
| 句 | 意味 |
|---|---|
private | スレッドごとに未初期化のコピーを作成 |
shared | すべてのスレッドで共有する単一の変数 |
reduction(op:var) | 終了時に結合(+, *, max, &&, ||) |
firstprivate | マスターから初期化 |
lastprivate | マスターが最後の反復の値を取得 |
4. SIMD ベクトル化ヒント
#pragma omp simd
for (int i = 0; i < n; i++)
c[i] = a[i] + b[i];
// SIMD + parallel
#pragma omp parallel for simd
for (int i = 0; i < n; i++)
c[i] = a[i] * b[i];
コンパイラーの SIMD サポートを使用するには、-fopenmp-simd または -fopenmp が必要です。-fopt-info-vec で確認してください。
5. タスク並列処理
#pragma omp parallel
{
#pragma omp single
{
for (int i = 0; i < 10; i++) {
#pragma omp task firstprivate(i)
process_subtree(i);
}
#pragma omp taskwait
}
}
タスクは、ループ並列処理が適さない再帰アルゴリズム(クイックソート、ツリー走査)に適しています。
6. 時間計測
double start = omp_get_wtime();
#pragma omp parallel for
for (int i = 0; i < n; i++) work(i);
double elapsed = omp_get_wtime() - start;
printf("elapsed: %f s\n", elapsed);
7. GPU target オフロード(OpenMP 5.x)
#pragma omp target teams distribute parallel for map(to:a[0:n]) map(from:c[0:n])
for (int i = 0; i < n; i++)
c[i] = a[i] * 2.0f;
# NVIDIA offload
gcc -fopenmp -foffload=-march=sm_80 -o offload offload.c
# Check device
export OMP_DEFAULT_TARGET_DEVICE=1
コンパイラーのオフロードサポート(GCC オフロード、Clang/OpenMP、NVIDIA HPC SDK)が必要です。
8. 環境変数
export OMP_NUM_THREADS=16
export OMP_PROC_BIND=close # bind threads to nearby cores
export OMP_PLACES=cores
export GOMP_SPINCOUNT=2000 # spin before sleep
export OMP_WAIT_POLICY=active # active vs passive waiting
export OMP_DISPLAY_ENV=true # print config at startup
9. プロファイリング
# Score-P (compile with wrapper)
scorep gcc -fopenmp -o app app.c
export SCOREP_METRIC_MANAGER=1
scorep ./app
scorep-score -f scorep_*/profile.cubex
# TAU
tau_cc.sh -fopenmp -o app app.c
export TAU_TRACE=1
./app
pprof app profile.*
10. 落とし穴
フォルスシェアリング:スレッドが隣接するキャッシュラインを変更します。
// Bad: sum_array[tid] on same cache line
#pragma omp parallel
{
int tid = omp_get_thread_num();
sum_array[tid] += local_sum; // pad to 64 bytes between elements
}
// Fix: padding
double sum_padded[MAX_THREADS][8]; // 8 doubles = 64 bytes
ネストした並列処理:
export OMP_MAX_ACTIVE_LEVELS=2
export OMP_NESTED=true # deprecated; use MAX_ACTIVE_LEVELS
よくある問題
| 症状 | 原因 | 対処法 |
|---|---|---|
| 高速化しない | ループが小さすぎる | 作業量を増やし、if 句のしきい値を確認する |
| リダクション結果が正しくない | リダクション対象外の変数に対する競合 | reduction または atomic を使用する |
| スレッド数を増やすと遅くなる | フォルスシェアリング | スレッドごとの配列にパディングを追加する |
| GPU オフロードに失敗する | ターゲットデバイスがない | -foffload と nvidia-smi を確認する |
| スレッドがバインドされない | デフォルトの分散設定 | OMP_PROC_BIND=close を設定する |
| ネストした並列処理でデッドロックする | オーバーサブスクリプション | 各レベルで OMP_NUM_THREADS を制限する |
関連スキル
skills/hpc/mpi— 分散メモリー並列処理を補完skills/low-level-programming/cpu-cache-opt— フォルスシェアリングの詳細skills/gpu/cuda— target オフロードに代わる GPU プログラミングskills/profilers/intel-vtune-amd-uprof— VTune での OpenMP 領域の解析skills/compilers/gcc—-fopenmpフラグskills/allocators/numa-programming— NUMA を考慮したスレッドバインド
コメント
Markdown 対応