• 検索結果がありません。

プロファイルツール実行例アプリ pspatiocyte 2013 年 10 月 10 日日本電気株式会社 0. はじめに 本ドキュメントでは, アプリ pspatiocyte におけるプロファイルツール連携の作業履歴を記載 します. 目次 1. TSUBAME2.0 通常実行まで

N/A
N/A
Protected

Academic year: 2021

シェア "プロファイルツール実行例アプリ pspatiocyte 2013 年 10 月 10 日日本電気株式会社 0. はじめに 本ドキュメントでは, アプリ pspatiocyte におけるプロファイルツール連携の作業履歴を記載 します. 目次 1. TSUBAME2.0 通常実行まで"

Copied!
22
0
0

読み込み中.... (全文を見る)

全文

(1)

プロファイルツール実行例 アプリ「pSpatiocyte」 2013 年 10 月 10 日 日本電気株式会社

0. はじめに

本ドキュメントでは,アプリ「pSpatiocyte」におけるプロファイルツール連携の作業履歴を記載 します.

目次

1. TSUBAME2.0‐通常実行まで ... 2 1.1. 環境設定(MPI ライブラリ&コンパイラ) ... 2 1.2. コンパイルとソース修正 ... 2 1.2.1. Makefile の修正 ... 2 1.3. バッチジョブ実行 ... 3 1.4. 実行結果の確認 ... 4 2. TSUBAME2.0‐Scalasca 連携実行 ... 5 2.1. Scalasca 環境設定 ... 5 2.2. Scalasca 連携‐初期測定・特性調査... 6 2.2.1. 調査の背景 ... 6 2.2.2. コンパイラの設定(自動/手動 instrument の違い) ... 6 2.2.3. 初期測定(テスト実行) ... 7 2.2.4. 特性調査(遅延原因の分析) ... 8 2.3. Scalasca 連携‐プログラム特性に応じた区間指定 ... 12 2.3.1. コンパイラの設定(手動 instrument+OpenMP に関するオプションの追加) ... 12 2.3.2. ユーザー区間指定の追加(メインループ) ... 12 2.3.3. ユーザー区間指定の追加(カーネル部分) ... 14 2.3.4. ユーザー区間指定の追加(その他) ... 18 2.4. Scalasca 連携‐プログラム実行... 20 2.5. Scalasca 連携‐実行結果の表示(可視化) ... 21

(2)

1. TSUBAME2.0‐通常実行まで

1.1. 環境設定(MPI ライブラリ&コンパイラ)

最新の Open MPI と Intel コンパイラを使用するため,${HOME}/.bashrc 等で環境変数(パス等)を 設定します. ~設定例~ export SELECT_MPI=/usr/apps/openmpi/1.6.3/intel export PATH=${SELECT_MPI}/bin:$PATH export LD_LIBRARY_PATH=${SELECT_MPI}/lib:$LD_LIBRARY_PATH ※2013 年 10 月現在,デフォルトの Intel コンパイラの ver.は 13.0 です. 1.2. コンパイルとソース修正 TSUBAME2.0 環境におけるプログラム移植のための修正を記載します. 1.2.1. Makefile の修正 TSUBAME2 環境に合わせて,Makefile 中のコンパイラおよびオプションを修正しました. コン パイラオプションの設定内容は,修正前の最適化レベルと同等です. ~修正後:Makefile~ CC = icc CXX = mpic++

CXXFLAGS = -openmp -O2 -I/work0/GSIC/apps/boost/1_45_0/intel/include LDFLAGS = -openmp -O2

SRC = compartment.cpp lattice.cpp reaction.cpp species.cpp world.cpp parallelenvironment. cpp

HD1 = common.hpp molecule.hpp spatioevent.hpp

HD2 = DynamicPriorityQueue.hpp EventBase.hpp EventScheduler.hpp HDR = $(SRC:.cpp=.hpp) $(HD1) $(HD2)

OBJ = $(SRC:.cpp=.o)

(3)

1.3. バッチジョブ実行 コンパイルして作成した実行モジュール「testpara」を使用して,バッチジョブを実行します. 問題サイズと並列数の設定は以下の通りです. ~使用ケース~ 格子サイズ (Nx,Ny,Nz)=(960,960,960) 密度変数 occupancy=0.3 並列数 32ノード(MPI 64プロセス×OpenMP 6スレッド) ステップ数 Nsim=1000 ~バッチジョブスクリプト(go.sh)~ #!/bin/bash cd ${PBS_O_WORKDIR} export OMP_NUM_THREADS=6

mpirun -np 64 --report-bindings -bysocket -cpus-per-proc 6 -bind-to-core -x OMP_NUM_THREADS -hostfile ${PBS_NODEFILE} ./testpara >& ./log.mpi

・ 作業ディレクトリと同じパス(PBS_O_WORKDIR)に移動し,mpirun を実行します. ・ 環境変数 OMP_NUM_THREADS は,mpirun の「-x」オプションで全プロセスに渡します. ・ TSUBAME2(westmere)は,6 コア*2 ソケットの計 12 個の物理的 CPU 構成となっています.

bind に関する記述は Open MPI のオプションであり,ハイブリッド実行時の各プロセスをソ ケット毎に割り当てる設定となります.

※ その他,バッチジョブの詳細については省略します(詳細はマニュアルにて)

~バッチジョブ投入方法~

% t2sub -q S -W group_list=t2g-hp120261 -l walltime=01:00:00 -l select=32:ncpus=6:mpiprocs= 2:mem=50gb -l place=scatter ./go.sh

・ 「-q」でキュー名,「-W group_list」でグループ名を指定します(ワークショップ用リソー ス,およびアプリ FS の課題番号の情報に基づいて設定します) ・ 「-l select=32:ncpus=6:mpiprocs=2:mem=50gb -l place=scatter」で,2 プロセス&50GB のチャンクを 32 個分確保します.結果的に,32 ノード,MPI 64 プロセスの実行の設定と なります.使用時間(上限)は,「-l walltime」で設定します. ※ その他,バッチジョブの詳細については省略します(詳細はマニュアルにて)

(4)

1.4. 実行結果の確認

バッチジョブのログ(OTHERS.e*,OTHERS.o*)と mpirun の実行ログ(log.mpi),および pSpatiocyte 側の実行ログ(ランク 0 番:xout0000)を確認し,プログラムが問題なく動作していることを確認しま した. ~実行ログ(xout0000)抜粋~ : Scheduled times =========================== ===== main loop ===== ===========================

Elapsed time = 119.968 sec

diffusion = 119.938 sec 1000 calls (calc = 104.867 sec)

(comm = 15.0714 sec) (pack = 5.23535 sec) (mpi = 9.25773 sec) (upck = 0.558764 sec)

independent = 0 sec 0 calls influenced = 0 sec 0 calls

============================== ===== results ===== ==============================

Statistics: elapse diffusion calc comm pack mpi unpack 0 119.968 119.938 104.867 15.0714 5.23535 9.25773 0.558764 1 119.968 119.913 104.798 15.115 5.64297 8.59457 0.858299 2 119.968 119.922 105.701 14.2207 5.50178 7.83396 0.866567 3 119.967 119.768 105.138 14.6295 5.51416 8.50697 0.586926 4 119.968 119.918 105.384 14.5342 5.38497 8.43149 0.697726 5 119.968 119.913 105.959 13.9547 5.90338 7.0221 1.00928 6 119.968 119.851 104.935 14.9155 5.70113 8.15917 1.03576 7 119.967 119.722 104.548 15.1741 5.59562 8.86493 0.692937 :※中略※ 60 119.968 119.733 105.613 14.1204 5.22058 8.3172 0.562604 61 119.968 119.665 105.646 14.0185 5.75388 7.34948 0.895415 62 119.968 119.48 104.825 14.6551 5.54281 8.19246 0.900836 63 119.968 119.437 104.888 14.5496 5.4442 8.52105 0.562564 max 119.968 119.938 106.586 15.2982 6.0403 9.25773 1.1677 min 119.967 119.437 104.412 13.3411 5.22058 6.81818 0.556476 ave 119.968 119.785 105.337 14.4487 5.62074 7.95731 0.850543

(5)

2. TSUBAME2.0‐Scalasca 連携実行

2.1. Scalasca 環境設定 Scalasca は,アプリ FS 共用にインストールしたパッケージを使用します.節 1.1 の環境設定に加 えて,${HOME}/.bashrc 等で環境変数(パス等)を設定します. ~設定例~ export PATH=/work1/t2g-hp120261/fs_share_tool/scalasca-1.4.3/bin:${PATH} export PAPI_ROOT=/work0/GSIC/apps/papi-5.0.0 export PATH=${PATH}:${PAPI_ROOT}/bin export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${PAPI_ROOT}/lib export LIBRARY_PATH=${LIBRARY_PATH}:${PAPI_ROOT}/lib export FPATH=${FPATH}:${PAPI_ROOT}/include export CPATH=${CPATH}:${PAPI_ROOT}/include export MANPATH=${MANPATH}:${PAPI_ROOT}/man ※2013 年 10 月現在,Scalasca は ver.1.4.3 がインストールされています. ※HW カウンタの情報は,PAPI を利用して採取しています.PAPI の環境設定は不要ですが,異なる PAPI の ver.を設定している場合には,削除する必要があります.

(6)

2.2. Scalasca 連携‐初期測定・特性調査 2.2.1. 調査の背景 アプリ「pSpatiocyte」では,Scalasca の自動 instrument(区間設定)機能を利用した場合,大 幅な実行時間の遅延が発生しています.一般的に,プロファイルツールを利用した場合に,遅延の 原因として考えられるのは以下の点です. ・ 少ない処理量の関数を多くの回数呼び出す場合 ・ 実行時間や回数だけでなく,その他多くの情報量を抽出する場合 (オーバーヘッドが大きい計測項目の全体に占める割合が高い場合) ここでは,事前に小規模モデル(問題サイズ・繰り返し回数)のプログラム実行を行い,アプリ 「pSpatiocyte」に掛かる Scalasca(プロファイル機能)のオーバーヘッドを観察します. 2.2.2. コンパイラの設定(自動/手動 instrument の違い) Scalasca を利 用し たコン パイ ルは ,基本 的には mpif90 等 コン パイ ラのコ マン ドの 前に 「scalasca -instrument」(もしくは短縮形「skin」)を追加するのみです.instrument 方法(自動 /手動)に応じて,以下のように Makefile の該当部分を修正します. ~修正前:Makefile~ CC = icc CXX = mpic++

CXXFLAGS = -openmp -O2 -I/work0/GSIC/apps/boost/1_45_0/intel/include LDFLAGS = -openmp -O2

~修正後①:Makefile(自動 instrument 用)~ CC = skin icc

CXX = skin mpic++

CXXFLAGS = -openmp -O2 -I/work0/GSIC/apps/boost/1_45_0/intel/include LDFLAGS = -openmp -O2

※デフォルトで(「-comp」の設定を省略した場合),「-comp=all」に相当します.

~修正後②:Makefile(手動 instrument 用)~ CC = skin -comp=none -user icc

CXX = skin -comp=none -user mpic++

CXXFLAGS = -openmp -O2 -I/work0/GSIC/apps/boost/1_45_0/intel/include LDFLAGS = -openmp -O2

(7)

2.2.3. 初期測定(テスト実行) 異なる instrument 方法によって作成された実行モジュールを使って,バッチジョブを実行し所 要時間の比較を行います. 節 1.3 にある使用ケースの設定から,問題サイズとステップ数を縮小した複数のケースを実行 しました. ~使用ケース~ 格子サイズ (6 種類) (Nx,Ny,Nz)=(100,100,100),(120,120,120),(150,150,150), (200,200,200),(240,240,240),(320,320,320) 密度変数 occupancy=0.3 並列数 8 ノード(MPI 16 プロセス×OpenMP 6 スレッド) ステップ数 Nsim=100 ~メインループの「Elapsed time」一覧~ 格子サイズ ケース 0(修正前) ケース 1(修正後①) ケース 2(修正後②) (100,100,100) 0.14 秒 76.49 秒 1.21 秒 (120,120,120) 0.16 秒 122.17 秒 2.01 秒 (150,150,150) 0.26 秒 223.31 秒 3.85 秒 (200,200,200) 0.54 秒 462.64 秒 8.75 秒 (240,240,240) 0.89 秒 750.70 秒 14.83 秒 (320,320,320) 1.95 秒 1724.55 秒 35.33 秒 ケース 0 の通常実行に比べて,ケース 1 の自動 instrument を有効にした場合の所要時間が大き く延びています.そこで,手動 instrument によるプロファイル採取をしようと考えましたが,ケ ース 2 の手動 instrument を追加していない場合にも,ケース 1 ほどではないものの遅延が発生し ています.プロファイル結果の信頼性を高めるため,遅延原因の調査と改善を進めます. なお,ケース 2 がケース 0 と一致しない理由は,「-comp=none」オプションで無効となる対象が ユーザー関数であり,MPI 通信関数や OpenMP 並列化ループの計測は有効となっているためです (「-comp=none」オプションでは無効にはなりません). ・ MPI 通信関数は,Scalasca のインストール時に計測ルーチンが埋め込まれています. ・ OpenMP 並列化ループは,Scalasca に同梱されている「OPARI2」の処理によって自動的に埋

め込まれています.デフォルトでは,並列化を構成する全ての要素(master,critical, sync 処理等)で個別に設置されます.

(8)

2.2.4. 特性調査(遅延原因の分析) アプリ「pSpatiocyte」のソースコードを含めて調査を行い,遅延原因に関する分析を行いまし た.結果として,以下①,②がケース 1,2 それぞれの原因となっていることが分かりました. ① 「-comp=all」オプションに相当する場合,inline 化される関数や C++の標準関数にも計測 ルーチンが埋め込まれる. コストの追跡調査を行い発生箇所を特定した結果,3 重ループ内で Lattice::linearCoord 関 数の呼び出しを行っている.処理の少ない関数であるため inline 化されているが,前後に計 測ルーチンが設置されて負荷の増大に繋がっている.また,C++の標準関数でも同様の問題が 発生している. ~ソースコード'lattice.cpp'の抜粋~ // parallel constructer with ghost cells

Lattice::Lattice(string s, double r, ParallelEnvironment &pe) {

name_ = s;

Nx_ = pe.getispan() + 2*GHOST_SIZE; Ny_ = pe.getjspan() + 2*GHOST_SIZE; Nz_ = pe.getkspan() + 2*GHOST_SIZE;

:※中略※

for(int i=0; i<Nx_; i++) for(int j=0; j<Ny_; j++) for(int k=0; k<Nz_; k++) {

const int coord = linearCoord(i,j,k);

:※中略※ /* * mid-plane */ voxelVector_[coord].adjacent[3] = linearCoord(iw, j+1,k); // NW voxelVector_[coord].adjacent[4] = linearCoord(ie, j+1,k); // NE voxelVector_[coord].adjacent[5] = linearCoord(i+1,j, k); // E voxelVector_[coord].adjacent[6] = linearCoord(ie, j-1,k); // SE voxelVector_[coord].adjacent[7] = linearCoord(iw, j-1,k); // SW voxelVector_[coord].adjacent[8] = linearCoord(i-1,j, k); // W if(k%2==0) { /* * top-plane */ voxelVector_[coord].adjacent[0] = linearCoord(i, j, k+1); // TN voxelVector_[coord].adjacent[1] = linearCoord(ie,j-1,k+1); // TE

(9)

voxelVector_[coord].adjacent[2] = linearCoord(iw,j-1,k+1); // TW /* * bottom-plane */ voxelVector_[coord].adjacent[ 9] = linearCoord(i, j, k-1);// BN voxelVector_[coord].adjacent[10] = linearCoord(ie,j-1,k-1);// BE voxelVector_[coord].adjacent[11] = linearCoord(iw,j-1,k-1);// BW :※中略※ } } } 計測ルーチンの挿入は,Intel コンパイラの場合「-tcollect」オプションで抑止可能だが, C++の標準関数も含めて除外する必要があり現実的ではない(FX10 では同様の回避方法が存在 しない).アプリ全体が C++で記述されている場合には,自動 instrument を無効にし手動 instrument でのプロファイル採取を考える. ② OpenMP 並列化されたループの中にある critical 処理区間の前後にも,個別に計測ルーチン が埋め込まれる. アプリ「pSpatiocyte」で唯一 OpenMP 並列化の対象となっているループは,「繰り返し回数が 大きい」,「ループ 1 回あたりの処理時間が短い」特徴を持っている.その内部に,OpenMP の critical 指示構文が含まれており,前後に計測ルーチンが設置されて負荷の増大に繋がって いる. ~ソースコード'compartment.cpp'の抜粋~

void Compartment::walk(Species &s, Lattice &g, ParallelEnvironment &pe) {

pe.starttimerdiff();

:※中略※

#pragma omp parallel {

vector<Molecule>::iterator p3 = p2save; const int max_thread = omp_get_num_threads(); const int thread = omp_get_thread_num(); const int start = Nmole*thread/max_thread; const int end = Nmole*(thread+1)/max_thread; //proceed iterator

(10)

for(int i=start; i<end; i++) {

if (g.getVoxel(adj).comp_ID == compartmentID_

&& g.getVoxel(adj).mole_ID == -1) // vacant {

#pragma omp critical {

if (g.getVoxel(adj).comp_ID == compartmentID_ && g.getVoxel(adj).mole_ID == -1) // vacant {

// move to new location

g.getVoxel(adj).spec_ID = specID; g.getVoxel(adj).mole_ID = p3->getID(); // reset previous location

g.getVoxel(coord).spec_ID = -1; g.getVoxel(coord).mole_ID = -1; // update moleculear location p3->setlinearcoord(adj); #ifdef DIAGNOSTICS fout << endl; #endif

// if molecule is running into ghost cells if (g.getVoxel(adj).ghos_ID == 1)

{

#ifdef DIAGNOSTICS

fout << "spilled out..." << endl; #endif

// remember dirty cells

dirty[ndirty ] = g.getXind(adj); dirty[ndirty+1] = g.getYind(adj); dirty[ndirty+2] = g.getZind(adj); dirty[ndirty+3] = g.getVoxel(adj).comp_ID; dirty[ndirty+4] = g.getVoxel(adj).spec_ID; dirty[ndirty+5] = g.getVoxel(adj).mole_ID; ndirty += 6; } } #ifdef DIAGNOSTICS

else fout << "failed to move..." << endl; #endif

} //end of critical

} // end of if

(11)

} // end of loop over molecules

} // end of parallel threads :

※アプリ「pSpatiocyte」では,parallel 指示行と OpenMP 関数のみで並列化を実装しています

この OpenMP 化されたループはコストの大部分を占め評価の中心となるため,並列化を無効に する事は不可である.計測ルーチンの呼び出し回数が大きくなり過ぎないように,ciritical 指示構文に対する計測ルーチンのみ無効にしたプロファイル採取を考える.

(12)

2.3. Scalasca 連携‐プログラム特性に応じた区間指定

2.3.1. コンパイラの設定(手動 instrument+OpenMP に関するオプションの追加)

手動 instrument 時のオプション設定に,OpenMP の critical 指示区間を抑止するオプションを 追加します.Makefile の該当部分を修正しました.

~修正後③:Makefile~

CC = skin --disable=critical -- -comp=none -user icc CXX = skin --disable=critical -- -comp=none -user mpic++

CXXFLAGS = -openmp -O2 -I/work0/GSIC/apps/boost/1_45_0/intel/include LDFLAGS = -openmp -O2

:: ※「--disable=critical --」の記述は,「OPARI2」の処理を制御するオプションです. 以降の節で,調査結果を基にして,ソースコードにユーザー区間指定を追加していきます. ※ 基本的なユーザー区間指定の方法については省略します(別途資料にて) 2.3.2. ユーザー区間指定の追加(メインループ) プログラムのメインループを,1 つのユーザー区間として指定します. ~修正後:testpara.cpp 抜粋~ #include <mpi.h> #include <iostream> #include <vector> #include <map> #include <cmath> #include <cstdlib>

#include "epik_user.h" ★Scalasca/ユーザー区間指定のインクルードファイル #include "world.hpp" #include "compartment.hpp" #include "lattice.hpp" #include "species.hpp" #include "reaction.hpp" #include "spatioevent.hpp" #include "parallelenvironment.hpp" #include "common.hpp" using namespace std; EPIK_USER_REG(r_name00,"iteration_loop"); ★ユーザー区間の名称設定 //#define REPLICA_MODE // repliceted systems or monolithic system

// parallel file streams ofstream fout;

int main( int argc, char *argv[] ) {

(13)

// simulation events :

const int Nsim = 1000; ★繰り返し回数の指定 :

// lattice dimension :

const int Nx=960, Ny=960, Nz=960; // DEBUG for MONOLITH ★格子サイズの指定

:※中略※

// prepare parallel environment // ParallelEnvironment pe(Nx, Ny, Nz);

ParallelEnvironment pe(argc,argv,Nx, Ny, Nz); // fix for diaspar

:※中略※

fout << endl << "===========================" << endl << "===== main loop =====" << endl << "===========================" << endl;

pe.starttimer(); ★既存のタイマー呼び出し(メインループの開始)

EPIK_USER_START(r_name00); ★ユーザー区間の開始 // event loop

for( int n=0; n<Nsim; n++ ) { #ifdef REPLICA_MODE sch.step(); #else sch.step(pe); #endif } EPIK_USER_END(r_name00); ★ユーザー区間の終了 pe.stoptimer(); ★既存のタイマー呼び出し(メインループの終了)

fout << endl << "Program Successfully Ended." << endl;

return 0; }

(14)

2.3.3. ユーザー区間指定の追加(カーネル部分) 唯一 OpenMP 並列化の対象となっているループは,アプリ「pSpatiocyte」の主要演算部分です. 評 価 対 象 の カ ー ネ ル と 考 え て , 当 該 箇 所 が 存 在 す る 関 数 の 呼 び 出 し 側 と 「 #pragma omp parallel」指示区間 をユーザー区間として指定します. ~修正後:spatioevent.hpp 抜粋~ #ifndef __SPATIOEVENT_HPP #define __SPATIOEVENT_HPP #include <iostream>

#include "epik_user.h" ★Scalasca/ユーザー区間指定のインクルードファイル #include "compartment.hpp" #include "lattice.hpp" #include "species.hpp" #include "reaction.hpp" #include "common.hpp" #include "EventScheduler.hpp" #include "EventBase.hpp" using namespace std; EPIK_USER_REG(r_name01,"reg_outer_walk"); ★ユーザー区間の名称設定 #undef DIAGNOSTICS

struct SpatiocyteEvent: public EventBase {

// diffusion event

SpatiocyteEvent(int id, Lattice* latt, Compartment* comp, Species* spec) { etype_ = DIFFUSION; id_ = id; dt_ = comp->getDiffusionTime(*spec); pspec_ = spec; pcomp_ = comp; platt_ = latt; name_ = spec->getName();

setTime(dt_); // dt as initial time in EventBase }

:※中略※

void fire(ParallelEnvironment &pe) {

#ifdef DIAGNOSTICS

fout << endl << "Fire! time = "

<< setw(8) << setprecision(4) << showpoint << left << getTime();

// fout << " : dt = " << dt_ ; fout << " : id = " << id_ << " : type = " << etype_ ;

(15)

#endif

setTime( getTime() + dt_ ); // update time in EventBase

switch(etype_) {

case DIFFUSION:

#ifdef DIAGNOSTICS

fout << " : diffusion " << name_ << endl; fout << "\t>>>> walk invoked <<<<" << endl; #endif

EPIK_USER_START(r_name01); ★ユーザー区間の開始

pcomp_->walk(*pspec_, *platt_, pe); ★対象の関数呼び出し側 EPIK_USER_END(r_name01); ★ユーザー区間の終了

break;

case INDEPENDENT_REACTION: #ifdef DIAGNOSTICS

fout << " : independent-reaction " << name_ << endl; fout << "\t>>>> independentReaction invoked <<<<" << endl; #endif

pcomp_->independentReaction(*platt_, pe); break;

case INFLUENCED_REACTION: #ifdef DIAGNOSTICS

fout << " : influenced-reaction " << name_ << endl; fout << "\t>>>> influencedReaction invoked <<<<" << endl; #endif

pcomp_->influencedReaction(*pspec_, *platt_, pe); break;

} }

private: int id_;

EVENT_TYPE etype_; // DIFFUSION, COLLISION, REACTION double dt_; Species* pspec_; Compartment* pcomp_; Lattice* platt_; string name_; }; #endif /* __SPATIOEVENT_HPP */ ※spatioevent.hpp は,testpara.cpp からのみインクルードされるファイルです

(16)

~修正後:'compartment.cpp'の抜粋~

#include "epik_user.h" ★Scalasca/ユーザー区間指定のインクルードファイル #include "compartment.hpp" #include <omp.h> #undef DIAGNOSTICS EPIK_USER_REG(r_name02,"reg_pragma_omp_parallel"); ★ユーザー区間の名称設定 EPIK_USER_REG(r_name03,"reg_pragma_omp_critical"); ★(未使用) :※中略※

void Compartment::walk(Species &s, Lattice &g, ParallelEnvironment &pe) {

pe.starttimerdiff();

:※中略※

EPIK_USER_START(r_name02); ★ユーザー区間の開始 #pragma omp parallel ★OpenMP並列化区間

{

vector<Molecule>::iterator p3 = p2save; const int max_thread = omp_get_num_threads(); const int thread = omp_get_thread_num(); const int start = Nmole*thread/max_thread; const int end = Nmole*(thread+1)/max_thread;

//proceed iterator

for(int i=0; i<start; i++) { p3++; }

for(int i=start; i<end; i++) {

if (g.getVoxel(adj).comp_ID == compartmentID_

&& g.getVoxel(adj).mole_ID == -1) // vacant {

// EPIK_USER_START(r_name03); ★ユーザー区間の開始(未使用) #pragma omp critical ★critical指示区間

{

if (g.getVoxel(adj).comp_ID == compartmentID_ && g.getVoxel(adj).mole_ID == -1) // vacant {

// move to new location

g.getVoxel(adj).spec_ID = specID; g.getVoxel(adj).mole_ID = p3->getID(); // reset previous location

g.getVoxel(coord).spec_ID = -1; g.getVoxel(coord).mole_ID = -1; // update moleculear location p3->setlinearcoord(adj);

(17)

#ifdef DIAGNOSTICS fout << endl; #endif

// if molecule is running into ghost cells if (g.getVoxel(adj).ghos_ID == 1)

{

#ifdef DIAGNOSTICS

fout << "spilled out..." << endl; #endif

// remember dirty cells

dirty[ndirty ] = g.getXind(adj); dirty[ndirty+1] = g.getYind(adj); dirty[ndirty+2] = g.getZind(adj); dirty[ndirty+3] = g.getVoxel(adj).comp_ID; dirty[ndirty+4] = g.getVoxel(adj).spec_ID; dirty[ndirty+5] = g.getVoxel(adj).mole_ID; ndirty += 6; } } #ifdef DIAGNOSTICS

else fout << "failed to move..." << endl; #endif

} //end of critical

// EPIK_USER_END(r_name03); ★ユーザー区間の終了(未使用) } // end of if

p3++;

} // end of loop over molecules

} // end of parallel threads

EPIK_USER_END(r_name02); ★ユーザー区間の終了 :

(18)

2.3.4. ユーザー区間指定の追加(その他)

プログラム側が有するサマリ機能(タイマーを使用して,コスト分布を採取)の各カテゴリーを, Scalasca 側のユーザー区間指定にも流用します.各カテゴリーのタイマー関数(開始/終了)の呼び 出し側に,Scalasca の計測ルーチンを併記します.

~修正後:lattice.cpp 抜粋~

#include "epik_user.h" ★Scalasca/ユーザー区間指定のインクルードファイル #include "lattice.hpp"

EPIK_USER_REG(r_name13,"reg_st_pack"); ★ユーザー区間の名称設定 EPIK_USER_REG(r_name14,"reg_st_mpic"); ★

EPIK_USER_REG(r_name15,"reg_st_upck"); ★ :※中略※

// load ghost cells

void Lattice::loadGhost( const int &xbegin, const int &xend, const int &ybegin, const int &yend, const int &zbegin, const int &zend, const bool &xbit,

const bool &ybit,

const bool &zbit, ParallelEnvironment &pe ) { :※中略※ //================================================================ // communication in x direction { #ifndef NOPACK

// pack data to be sent

pe.starttimerpack(); ★既存のタイマー呼び出し EPIK_USER_START(r_name13); ★ユーザー区間の開始 const int i0 = ( xbit==1 ? 1 : Nx );

const int i1 = ( xbit==1 ? Nx+1 : 0 ); int nsent = 0;

// sub-donar

for(int j=ybegin; j<=yend; ++j) for(int k=zbegin; k<=zend; ++k) {

const int coord = linearCoordFast(i0,j,k);

// append occupied voxel to send-buffer if( voxelVector_[coord].mole_ID != -1 ) { outbound[nsent ] = j; outbound[nsent+1] = k; outbound[nsent+2] = voxelVector_[coord].comp_ID; outbound[nsent+3] = voxelVector_[coord].spec_ID; nsent += 4;

(19)

} } EPIK_USER_END(r_name13); ★ユーザー区間の終了 pe.stoptimerpack(); ★既存のタイマー呼び出し #endif #ifndef NOCOMM // send and recieve

pe.starttimermpic(); ★既存のタイマー呼び出し EPIK_USER_START(r_name14); ★ユーザー区間の開始 const int left = pe.getneighbor_i_minus(); const int right = pe.getneighbor_i_plus(); const int to = ( xbit==1 ? left : right ); const int from = ( xbit==1 ? right : left );

reqs[0] = cart.Isend( outbound, nsent, MPI::INT, to, 0 ); reqs[1] = cart.Irecv( inbound, bufsize, MPI::INT, from, 0 ); // clear sub-ghost

for(int j=ybegin; j<=yend; ++j) for(int k=zbegin; k<=zend; ++k) {

const int coord = linearCoordFast(i1,j,k); voxelVector_[coord].comp_ID = -1;

voxelVector_[coord].spec_ID = -1; voxelVector_[coord].mole_ID = -1; }

MPI::Request::Waitall( 2, reqs, stat ); EPIK_USER_END(r_name14); ★ユーザー区間の終了 pe.stoptimermpic(); ★既存のタイマー呼び出し #endif

#ifndef NOUNPACK

// unpack recieved data

pe.starttimerupck(); ★既存のタイマー呼び出し EPIK_USER_START(r_name15); ★ユーザー区間の開始 const int nrecv = stat[1].Get_count(MPI::INT); for(int irecv=0; irecv<nrecv; irecv+=4)

{

const int j = inbound[irecv ]; const int k = inbound[irecv+1];

const int coord = linearCoordFast(i1,j,k); voxelVector_[coord].comp_ID = inbound[irecv+2]; voxelVector_[coord].spec_ID = inbound[irecv+3]; voxelVector_[coord].mole_ID = 99999; // dummy ID } EPIK_USER_END(r_name15); ★ユーザー区間の終了 pe.stoptimerupck(); ★既存のタイマー呼び出し #endif } :

(20)

2.4. Scalasca 連携‐プログラム実行

節 2.3 の修正内容によって作成された実行モジュールを使って,バッチジョブを実行します.使 用するケースとジョブの投入方法は,節 1.3 の通常実行と同じです.

Scalasca を利用したプログラム実行は,実行モジュール(逐次)や mpirun(MPI 並列)コマンドの前 に「scalasca -analyze」(もしくは短縮形「scan」)を追加して行います.

TSUBAME2.0 のバッチジョブでの実行では,Scalasca と mpirun のオプションが正しく識別できな いため,mpirun のオプションを「”(ダブルクォーテーション)」で囲う必要があります.混同を避け るため,Scalasca 側のオプションを指定する場合は,設定ファイル(EPIK.CONF)に書くことをお勧め します. ~バッチジョブスクリプト(go.sh)~ #!/bin/bash cd ${PBS_O_WORKDIR} export OMP_NUM_THREADS=6 export SCAN_SETENV="-x="

scan mpirun -np 64 "--report-bindings" "-bysocket -cpus-per-proc 6 -bind-to-core" "-x OMP_N UM_THREADS" "-hostfile ${PBS_NODEFILE}" ./testpara >& ./log.mpi

~設定ファイル(EPIK.CONF)の例~

EPK_TITLE=fs_pspatiocyte ★アーカイブ名

EPK_METRICS=PAPI_SP_OPS:PAPI_DP_OPS:PAPI_VEC_SP ★メトリックの指定(PAPIプリセットイベント) ESD_BUFFER_SIZE=100000000 ★バッファサイズの指定

(21)

2.5. Scalasca 連携‐実行結果の表示(可視化) X-window が使用 できるタ ーミナル を立ち 上げて, 出力され たアー カイブ (epik_*)に対し て 「scalasca -examine」(もしくは短縮形「square」)を実行します. ~コマンド実行例~ % square epik_fs_pspatiocyte ~表示画面~

(22)

採取した評価用モデル(問題サイズ・繰り返し回数)の Scalasca(プロファイル機能)実行結果につ いて,簡単な確認を行います. ~使用ケース~ 格子サイズ (Nx,Ny,Nz)=(960,960,960) 密度変数 occupancy=0.3 並列数 32ノード(MPI 64プロセス×OpenMP 6スレッド) ステップ数 Nsim=1000 ~ジョブ実行情報(手動 instrument 修正版ケース)~ 表示場所 内容・項目 値 備考

バッチジョブ情報 walltime 132[sec] Scalasca 無し:130[sec] used memory 2.627[Gbyte] Scalasca 無し:2.611[Gbyte] pSpatiocyte ログ Elapsed time 121.181[sec] Scalasca 無し:119.968[sec] du または ls コマンド Scalasca 関連

出力サイズ

約 240[Kbyte] 一回表示後に 300[Kbyte]前後になる

Scalasca 結果(time) カーネル部分 105.80[sec] OpenMP 並列化ループ.全体の約 87%

プログラム全体の 85%以上のコストが OpenMP 並列化されたループに集中しており,その区間のプ ロファイル採取が確認できました.また,その他設定したユーザー区間と MPI 通信関数についても, コールツリーに含まれていることが確認できました. Scalasca 連携実行で発生した実行時間の延びは,約 1.2 秒と全体の 1%程度に留まっており(誤差 によるものとも考えられます),採取したプロファイル結果の信頼性には問題がないと考えられます. また,Scalasca(プロファイル機能)によるファイル出力は,約 300[Kbyte]となり小さな量です.

参照

関連したドキュメント

構文 :SOURce:VOLTage:RANGe:AUTO 1|0|ON|OFF

本節では本研究で実際にスレッドのトレースを行うた めに用いた Linux ftrace 及び ftrace を利用する Android Systrace について説明する.. 2.1

(ページ 3)3 ページ目をご覧ください。これまでの委員会における河川環境への影響予測、評

(※)Microsoft Edge については、2020 年 1 月 15 日以降に Microsoft 社が提供しているメジャーバージョンが 79 以降の Microsoft Edge を対象としています。2020 年 1

家電製品を仕入れさせて頂ける企業様を探しています。 10月31日 埼玉県 1 全国 ゴマ、抹茶を活用した常温のお菓子を探しております。 10月31日

タップします。 6通知設定が「ON」になっ ているのを確認して「た めしに実行する」ボタン をタップします。.

全国の宿泊旅行実施者を抽出することに加え、性・年代別の宿泊旅行実施率を知るために実施した。

継続企業の前提に関する注記に記載されているとおり、会社は、×年4月1日から×年3月 31