 使用可能なファイルシステム
 バッチキューの操作(上級編) makeを使った分割コンパイル2010年9月3日
鴨志田 良和 (東京大学情報基盤センター) makeを使った並列処理

1234

 HA8000クラスタシステムにログインして効率的に作業を行えるようになることを目指し、◦ ファイルシステムやキューの操作について学ぶ 大規模なプログラムを作成する際に必須となる、分割コンパイルの方法について学ぶ makeを使用した並列処理の方法について学ぶ HA8000クラスタシステムで利用可能なファイル

 他のノードから直接アクセスできない記憶域

システムは以下のとおりである

 HA8000クラスタシステムにおける設定

PATH

種類

共有/非共有/home/ログイン名(*)

HSFS

共有

/short/ログイン名

HSFS

共有

/nfs/all/ログイン名(**)

NFS

共有

/lustre/ログイン名

Lustre共有/tmp

ローカルディスク 非共有◦ /tmpに置いたファイルは、ログインノードでは1〜2日で、計算ノードではジョブの終了時に削除されるディスクと直接接続(*) 負荷分散のため、ユーザごとに/hsfs/home[1-4] のいずれかを使用(**) 申込みにより、/nfs/personal/ログイン名、/nfs/グループ名/ログイン名も利用可能

目的に応じて使い分けると効率的に作業ができる

56

 ネットワーク経由で複数クライアントからアクセ 複数のファイルサーバにデータおよびメタデータス可能 動的な負荷分散機能がない(サーバは1台)
 HA8000クラスタシステムにおける設定を分散配置
 1ファイルのデータを複数台のサーバに分散可能
 フェイルオーバーにより、サーバの故障に対応可
メタデータ
能◦ ユーザ・グループごとに分けてサーバを4台使用

サーバ

オブジェクト
格納サーバ

ネットワーク経由で接続

NFSサーバオブジェクト格納サーバ78

 ローカルディスク

 HSFS(Hitachi Striping Filesystem)◦ 一時的なデータ置き場◦ 複数ノードから大きなブロックサイズ(数MB以上?)で入出力を行う場合に効率がよいとされている◦ 現在はノードあたりの最大帯域幅は1Gbps

 NFS◦ ログインノードにおける作業時やファイル操作のレスポンスが重視される場合に適している◦ 多数のクライアントからの並列アクセスには適さない 今後改善の計画あり◦ /shortは5日後に削除される(一時的なデータ置き場)
 Lustre

 分散ファイルシステム(HSFS, Lustre)◦ 2010年3月からサービス開始◦ 大規模ファイル入出力、メタデータ操作の両方で高性能なファイルシステム◦ データの分散方法をファイルごとに指定可能(後述)◦ 複数のファイルサーバにデータを分散できるため、多くのプロセスからアクセスする場合に効率がよい◦ 構成が複雑なため、NFSに比べると1クライアントからのアクセス性能は低い場合がある
ただし、1ファイルのデータを複数のサーバに分散させれば、1クライアントからの転送効率を上げることができる(lfs setstripeなど)
9 共有ファイルシステムは、個人、またはグループ それぞれのファイルシステムでファイル展開コマに対して利用可能容量の制限(quota)がある
 残り容量の確認コマンド
HSFS

la –a

NFS

quota –v

Lustrelfs quota /lustre//tmp

df /tmp10ンドを実行せよ◦ 実行時間にどのような差があるか？
 各ファイルシステムの残り容量を確かめよ

1112

◦ HA8000のLustreファイルシステムは30個のOST(ObjectStorage Target: 仮想的なディスク)で構成◦ それぞれのOSTはRAID6構成(8D+2P)◦ メタデータの格納先(MDT: Metadata Target)はRAID1構成 /home/t00004/public に、この講習会で使用したプログラム、課題の解答などのファイルを置きました。ご利用ください。

メタデータサーバ

オブジェクト格納サーバ13

 lfs setstripe -s size -c count ファイル名

 データ配置の指定◦ ファイルのデータをひとつのOSTに配置するか、複数のOSTに分散して配置するかはユーザが指定できる◦ デフォルトではひとつのOSTに配置◦ lfs getstripe / lfs setstripeコマンドで参照・変更可能

ひとつのOSTに配置

OST1

OST2

OST3

14

◦ size 毎にcount 個のOSTに渡ってデータを分散配置する設定にした空のファイルを作成する$ dd if=/dev/zero of=/lustre/$USER/4G.dat bs=1M count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 41.0291 seconds, 105 MB/s
OST数が1の場合の書き込み性能$ rm /lustre/$USER/4G.dat$ lfs setstripe -s 1M -c 4 /lustre/$USER/4G.dat
ストライプ設定の変更(4つのOSTにデータを分散)$ dd if=/dev/zero of=/lustre/$USER/4G.dat bs=1M count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 10.9823 seconds, 391 MB/s
OST数が4の場合の書き込み性能

複数のOSTに配置

OST1

OST2

OST3
1516 qstat

BATCH REQUEST: 253443.batch1
Name: test.sh
Owner: uid=33071, gid=30144
Priority: 63
State: 1(RUNNING)
Created at: Tue Jun 30 05:36:24 2009
Started at: Tue Jun 30 05:36:27 2009
Remain : 14 minutes 47 seconds-l ジョブID (qstat -f)◦ ジョブの、より詳しい状態を確認するコマンド◦ ジョブIDを指定しない場合は実行前・実行中の、自分のすべてのジョブが対象

QUEUE
Name:

 qscript

ジョブID

tutorial

Per-proc. volafile size limit = 0 kilobytes
Per-req. volafile size limit = 0 kilobytes
Per-proc. working set limit= 28 gigabytes
Per-req. etime limit= 15 minutes
warning = 0 seconds
Per-proc. execution nice pri. = 0FILES
Stderr: NoneStdout: Nonestderr to stdout: No

RESOURCES
MAIL
Per-proc. CPU time limit= UNLIMITED
Address: t00004@ha8000-3.cc.u-tokyo.ac.jp
warning = 0 seconds
When:
Per-req. CPU time limit= UNLIMITED
warning = 0 seconds
MISC
Per-proc. core file size limit= 0 kilobytes
Partition: None
Per-proc. data size limit= 28 gigabytes
Node: 1warning = 0 kilobytes
Jobtype: T1
Per-proc. perm file size limit= UNLIMITED
Rerunnable: Yes
warning = 0 kilobytes
Performance information: Yes
Per-proc. memory size limit= 28 gigabytes
Shell: None
Per-req. memory size limit= 28 gigabytes
Account name: f
Per-proc. stack size limit= 2 gigabytes
Qsub at: /nfs/all/t00004◦ 投入したスクリプトを確認するコマンド17

 qsub-N 2 -q debug スクリプト名18 qsub-q tutorial コマンドを実行し、標準入力に env|sort; sleep 30を入力してCtrl-D
キーで終了◦ debugキューの2ノードを使用して実行◦ ジョブスクリプトに書いたものより、コマンドライン引数で指定したオプションのほうが優先される◦ 注意: qscriptで表示されるものと実際のオプションが異なる場合がある◦ qstat -fを使って確認すれば、正しい情報が得られる

 qscriptでスクリプトを確認せよ
 qstat-lで詳細情報を確認せよ
 ジョブ終了後、STDIN.o??????に出力された内容を確認せよ◦ どのような環境変数が設定されているか

1920

 実行されたジョブのノード数(-N)とジョブタイプ(-J)を標準出力に表示するスクリプトを書け

2221

 プログラムの分割コンパイル等を支援するソフトウェア
 変更があったファイルのみを再コンパイル
 大規模なプログラムを書くときに便利 本質的にはワークフロー言語の実行エンジン
 コンパイルに限らず、処理の依存関係を記述して、依存関係に従ってコマンドを実行できる hello.c#include <stdio.h>
int main(int argc, char** argv) {printf("Hello, world!¥n");return 0;}
 Makefile
hello: hello.c
gcc -o hello hello.c◦ スペースでなくタブにする◦ この講習会ではGNU make (version 3.81)を使用する

 実行$ make hello
gcc -o hello hello.c

23

もう一度makeを実行するとどうなるか？
$ make hello
make: `hello' is up todate.
24

 ルールは、ターゲット、依存するファイル、コマ 以下のいずれかが満たされる場合にコマンドを実行ンドで記述される◦ ターゲットが存在しない◦ (ターゲットのタイムスタンプ )< (依存するいずれかのファイルのタイムスタンプ)

ターゲット: 依存するファイル …
コマンド…
 makeの実行

 依存するファイルXが存在しない場合、make◦ make ターゲット◦ ターゲットを省略した場合は、Makefileの最初のターゲットが指定されたものとして実行される

Xを先に実行
 コマンドを実行した後の終了ステータスが0以外の場合は続きの処理を実行しない25

 hello.c
 実行#include <stdio.h>$ makevoid hello(void) {gcc -c hello.c
printf("Hello, world!¥n");gcc -c main.c}
gcc -o hello hello.o main.o main.cvoid hello(void);
int main(int argc, char** argv) {  hello.cを書き換え
hello();
例: world! を world!! にreturn 0;
書き換え} Makefile
hello: hello.o main.ogcc -o hello hello.o main.o makeを再実行
hello.o: hello.c
gcc -c hello.c$ makemain.o: main.c
gcc -c hello.c
gcc -c main.c
gcc -o hello hello.o main.o27

26

 2回目のmakeで起きていたこと◦ main.oのコンパイルは、main.cに変更がなかったため行われなかった
 Makefileに依存関係を適切に記述することで、変更があった部分だけを再コンパイルすることができる28 Makefileの指定

module1.o: module1.c common.hgcc -o module1.o module1.c -cmodule2.o: module2.c common.hgcc -o module2.o module2.c -cmodule3.o: module3.c common.hgcc -o module3.o module3.c -ca.out: module1.o module2.o module3.o
gcc -o a.out module1.o module2.o module3.ocommon.hmodule1.c
module2.c

module1.o$ make -f test.mk 長い行
hello: hello.o main.ogcc -g -Wall -O3 ¥-o hello hello.o main.o

以下の場合に何が起きるか考えよ・module1.cが変更された場合
・module2.oが削除された場合

module2.o PHONYターゲット.PHONY: clean
clean:(cleanというファイルがあっても実行する)
rm –f hello hello.o main.o ディレクトリを移動してmake$ make –C hello2 target(cd hello2; make targetと同様)

a.out

module3.o

module3.c29

 コマンドの前のタブを、スペースにした場合、ど30

 変数、関数の使用・特別なルールの書き方のようなエラーが出力されるか .PHONY:

Xがあるときとない時で、make Xの動作に違いがあることを確認せよ

 Makefileのより簡潔な記述
 より柔軟な出力やエラー制御

3132

 代入方法

 実行しようとするコマンドを表示しない

OBJECTS=main.o hello.o 参照方法
hello: $(OBJECTS)

test1:@echo Test message${OBJECTS}でもよい$OBJECTSとすると、$(O)BJECTSと同じことになる コマンド終了時ステータスを無視する

test2:-rm file1 file2 file3

 再帰的な展開
CFLAGS=$(INCLUDES) -O -g
INCLUDES=-Idir1 -Idir2
CFLAGSは -Idir1 -Idir2 -O -gに展開される

3334

 変数と似た参照方法で利用可能

 コマンドの条件分岐
hello: $(OBJECTS)
ifeq ($(CC),gcc)$(CC) -o hello $(OBJECTS) $(LIBS_FOR_GCC)
else$(CC) -o hello $(OBJECTS) $(LIBS_FOR_OTHERCC)
endif 変数代入の条件分岐
ifeq ($(CC),gcc)
LIBS=$(LIBS_FOR_GCC)
else
LIBS=$(LIBS_FOR_OTHERCC)
endif

VALUE=$(subst xx,yy,aaxxbb)

VALUEにaayybbが代入される

CONTENTS=$(shell cat data.txt) CONTENTSにはdata.txtの中身が代入される
SECOND=$(word 2, This is a pen) SECOND=isと同じ
CDR=$(wordlist 2,$(words $(LIST)), $(LIST))CDRには$LISTの2番目以降の単語のリストが代入される
 他の関数の例◦ dir, notdir: シェルのdirname, basenameに似た動作◦ suffix, basename: 拡張子とそれ以外の部分に分ける

 利用可能なディレクティブ◦ ifeq, ifneq, ifdef, ifndef

 シェルのbasenameとは違う
35◦ wildcard: ワイルドカードを展開

36

 ターゲット名や依存ファイル名などに展開される

 指定したパターンにマッチしたらコマンドを実行

特殊な変数がある
$@
$<$?
$+する
%.o : %.c$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ターゲット名最初の依存ファイルターゲットより新しい依存ファイル
すべての依存ファイル

hello: hello.o main.ogcc -o hello ¥hello.o main.o
hello.o: hello.c
gcc -c hello.c
main.o: main.c
gcc -c main.c◦ ***.oは***.cに依存する
hello: hello.o main.ogcc -o hello hello.o main.o
hello.o: hello.c
gcc -c hello.c
main.o: main.c
gcc -c main.c

CC=gccOBJECTS=hello.o main.o
hello: $(OBJECTS)$(CC) -o $@ $+hello.o: hello.c$(CC) -c $<main.o: main.c$(CC) -c $<CC=gccOBJECTS=hello.o main.o
hello: $(OBJECTS)$(CC) -o $@ $+%.o: %.c$(CC) -c $<3738

 以下のルールDATE1をDATE2,DATE3に変更し

DATE1 = $(shell date)
DATE2 := $(shell date)
DATE3 = `date`て実行せよ。2つechoの出力に違いはあるか？
test:echo $(DATE1)
sleep 1echo $(DATE1)

 DATE1◦ 参照されるたびにdateが実行される◦ 実行されるタイミングは最初(アクションが実行される前) DATE2

 DATE1,DATE2は、一見すると出力が同じであ◦ 1度だけdateが実行される◦ 実行されるタイミングは最初るが、どうすれば動作の違いを説明できるか？
 DATE4:= `date`

 DATE3◦ はどれと同じ動作になるか◦ 最初は̀datèという文字列が展開されるだけ◦ Dateが実行されるのは各アクションが実行されるとき
39

40

 wildcard関数を使用して以下の処理を行う

Makefileを記述せよ◦ 入力データの中から、2009年8月と9月のデータだけを処理する
 入出力データの仕様◦ 入力ファイル名に日付が含まれている(YYYYMMDD.in)◦ 出力データは拡張子を.inから.outに変え、内容をコピーする

4241

 make makeは本質的にはワークフロー言語とその実行-j による並列化◦ 同時実行可能なコマンドを見つけて並列に実行◦ 依存関係の解析はmakeが自動的に行ってくれるエンジン◦ コンパイル以外にもいろいろなことができる
 makeを使うとうれしいこと

all: a b◦ 実行するコマンドの依存関係を簡単に記述可能◦ 簡単な並列化

a: a.c$(CC) a.c -o a

 依存関係の解析はmakeが自動的に行ってくれる◦ 耐故障性b: b.c$(CC) b.c -o b

 途中で失敗しても、makeし直せば続きからやってくれる

43

同時実行可能44

 makeGNU
make-j で指定した数まで同時に実行する

sh

コマンド

sh

コマンド

sh

コマンド

sh

コマンド

sh

コマンド

sh-j 最大並列度◦ 最大並列度で指定した数まで同時にコマンドを実行する◦ 最大並列度の最大値は4096(RHEL5における制約)
 それ以上を指定すると1を指定したものとみなされる◦ 省略した場合、可能な限り同時にコマンドを実行する(∞)
 make -jが正常に動作しない場合◦ Makefileの書き方の問題
 暗黙の依存関係
 同名の一時ファイル◦ リソース不足
 使用メモリやプロセス数が多すぎる 最大並列度を適切に設定する必要がある

コマンド4546

 逐次makeの実行順序に依存したMakefileの記述

 逐次make実行順序に依存するMakefileの別な例

をしてはいけない 左のターゲットから順番に処理されることに依存したMakefile: 同名の一時ファイルを使用すると、並列実行時に競合する
all: a b

all: 1.out 2.out
1.out:
sleep 1; echo Hello > 1.out
2.out: 1.out
cat 1.out > 2.out

a: a.c.gz
gzip –dc < a.c.gz > tmp.c$(CC) tmp.c -o a 本来は依存関係を明示する必要がある

b: b.c.gz
gzip –dc < b.c.gz > tmp.c$(CC) tmp.c -o b
47

tmp.cが競合

48

 Makefile HA8000クラスタシステムの場合、1ノードでは使えるCPUコア数は16まで
 多数のノードを使用すれば、よりたくさんの処理を行うことが可能
 GXP makeを使用すると複数ノードで並列makeを実行可能

FILE_IDS := $(shell seq 1 10)
FILES:= $(FILE_IDS:%=%.dat)
all: $(FILES)%.dat:◦ GXP makeは並列シェルGXPと一緒に配布されているソフトウェア◦ makeの処理を、マスターワーカー型の並列処理として複数ノードで実行可能◦ 各ノードでファイルが共有されていることが前提

sleep 5touch $@◦ 変数や%を使わない場合どのようなMakefileになるか◦ makeとmake -jの実行時間を比較せよ

49

 並列分散環境を簡単に扱うための、並列版シェル50

 各計算ノードでデーモンプロセス(GXPD)を起動◦ 多数のノードのインタラクティブな利用◦ 並列ワークフローの実行(GXP make)◦ ノード集合と、GXPDの起動方法を指定(use) SSH, PBS, GridEngine等が利用可能。拡張も可能◦ ノード集合を指定して、GXPDを起動(explore) 詳しい情報

http://www.logos.t.u-tokyo.ac.jp/gxp
http://sourceforge.net/projects/gxp

 e(execute)コマンドでユーザプロセスを起動◦ 全部または一部のノードを指定可能

 ダウンロード方法$ cd /nfs/all/$USER$ cvs -d ¥:pserver:anonymous@gxp.cvs.sourceforge.net:/cvsroot/gxp ¥co gxp3
 HA8000上のインストール先
各ノードの /opt/itc/gxp3
51

SH

use
exploreexecute

RSH
RSH

SSH
TORQUE

RSH
RSH52

 Exploreするところまでを実行するスクリプト#@$-N 4#@$-J T1

ha8000̲gxp.sh を使用すれば、より簡単に記述可能

NODES=`qstat -f $PBS_JOBID|grep Node:|awk '{print$2}'`

ノード数を取得
gxpc --root_target_name headgxpc use torque_psched head node
gxpc explore node[[1--$NODES]]

各ノードでGXPDを起動#@$-N 4#@$-J T1
. /opt/itc/bin/ha8000_gxp.sh

cd $QSUB_WORKDIR
gxpc cd `pwd`
gxpc e 'echo $GXPC_EXEC_IDX `hostname`'
gxpc quit

headnode1

コマンドの実行

torque̲pschedはtorque
ジョブ内のプロセス起動

cd $QSUB_WORKDIR
gxpc cd `pwd`gxpc e 'echo $GXPC_EXEC_IDX `hostname`'
gxpc quit

node2

node3
53

54

mksh

 makeで実行される各コマンドをGXP経由で実行◦ -jオプションと組み合わせて、ノードにまたがってmakeを並列実行することができる◦ 各ノードでファイルが共有されている必要がある

gxpc

コマンド

gxpc

コマンド

gxpc

コマンド

gxpc

コマンド

mksh

gxpc

コマンド

mksh

gxpc

コマンド

mksh
GNU
makemkshmksh

 gxpc make …

◦ … には、GNU makeに渡すことができるすべてのオプションを渡すことができる

GXP
make55

直接コマンドを実行せずス
ケジューラに登録するだけ

xmake
スケ
ジューラ

どのノードでコマンド 指定されたノードでコマンを実行するか決める
ドを実行するコマンド56 以下に述べる並列処理を実行せよ#@$-N 4#@$-J T1

 処理の内容cd $QSUB_WORKDIR. /opt/itc/bin/ha8000_gxp.sh◦ 複数の入力ファイルがある(in/inpXX-Y.dat)◦ 入力ファイルごとに、その内容に従って処理を行い、1つの出力ファイルを生成する(out/outXX-Y.dat)

GXPDの起動echo cpu . 16 > gxpc_make.conf

 入力ファイルの内容により、処理時間は異なる

ノードあたり16プロセス起動

gxpc cd `pwd`
gxpc make -j
gxpc quit◦ それぞれのタスクは独立で、並列実行可能 以下のそれぞれの場合を実際に試して、実行時間

並列makeの実行の違いの理由を考えよ◦ 処理するファイルをプロセスごとに固定する場合(MPI)◦ マスターワーカー型の負荷分散を行う場合(GXP make)57

50

100経過時間(秒)
150

200

250

300

00010102020
プロセス番号

プロセス番号05830
40100経過時間(秒)150200250300304050506060595060

050100

経過時間(秒)
150200250300 容易にパラメタ並列処理を記述可能◦ GXPが提供する、パラメタ並列用のMakefileをincludeする010

 使用方法◦ parameters, target, output, cmd 変数を定義する◦ output, cmdは、:=ではなく=で値を定義する

プロセス番号

20

 これをテンプレートとして何度も展開される

30◦ $(GXP̲MAKE̲PP)をinclude文で読み込む
 (GXPインストール先)/gxpmake/gxp̲make̲pp̲inc.mk

40
5060
61

 例1: (2 * 3 * 4=24個のタスクを並列実行)

62

 MapReduceモデル◦ 以下のMakefileを書いて、gxpc make -j baz を実行する◦ Googleが提案する、大規模データの並列処理に特化したプログラミングモデル◦ 1レコードに対する処理を書くと、処理系が大規模データに並列適用◦ 入力データは、レコードの集合◦ プログラムは、以下の2つの処理を定義

parameters:=a b ca:=1 2b:=3 4 5c:=6 7 8 9target:=baz
output=hoge.$(a).$(b).$(c)cmd=expr $(a) + $(b) + $(c) > hoge.$(a).$(b).$(c)include $(GXP_MAKE_PP)

 Map: レコード→(key, value)の集合
 Reduce: (key, value)の集合→出力
 異なるレコードに対するmap処理と、異なるkeyに対するreduce処理が並列実行可能

 例2: (課題8の処理)◦ 複数のパラメタ並列処理の組み合わせも可能

63

64

 include $(GXP̲MAKE̲MAPRED)の前に、以下の

 GXP make上に構築されたMapReduce処理系

変数を設定する◦ パラメタ並列と同様に、GXPが提供するMakefileをincludeするだけで利用可能◦ GXPが動く環境ならどこでも動く◦ input=入力ファイル名◦ output=出力ファイル名◦ mapper=mapperコマンド(ex̲word̲count̲mapper)◦ reducer=reducerコマンド(ex̲count̲reducer)◦ n̲mappers=map ワーカ数(3)◦ n̲reducers=reduce ワーカ数(2)◦ int̲dir=中間ファイル用ディレクトリ名

 カスタマイズが容易◦ Makefileと、mapper,reducerなどのいくつかの小さなスクリプトを書くだけ 省略時は$(output)̲int̲dir◦ keep̲intermediates=yの時、中間ファイルを消さない◦ small̲step=yの時、細かいステップでの実行

65

66

 ファイルシステムやバッチキューイングシステム

 例(word count)◦ HA8000クラスタシステムに固有の情報を活用することで、より効率的なシステムの利用が可能◦ Mapper: レコード→(単語1, 1),(単語2, 1),…
◦ Reducer: それぞれのkeyについてvalueの和を出力◦ 以下のMakefileを書いて、gxpc make -j bar を実行する

 make, Makefile

input:=foo
output:=bar
mapper:=ex_word_count_mapper
reducer:=ex_count_reducer
n_mappers:=5
n_reducers:=3
include $(GXP_MAKE_MAPRED)◦ make, Makefileを利用することで、変更箇所だけを再作成する分割コンパイルが可能

 並列ワークフロー処理◦ make -jで並列にmake処理を実行可能◦ makeを拡張したGXP makeを利用することで、大規模な並列処理を実行可能

 複数のMapReduceやパラメタ並列処理を組み合わせることも可能
67

68