【耐故障並列ソフトウェアライブラリ】
by
杉野栄二、横田治夫 at JAIST (北陸先端科学技術大学院大学)
[用途]
一般の並列計算機上で、KL1プログラムの耐故障実行を実現する。
[機能]
サンプルプログラム ft_queen.kl1 のように
耐故障ソフトウェア化したKL1プログラムとリンクすることで、
耐故障実行を行う。
述語 distribution(Primary,Backup) : watchdog.kl1 で提供される
は、すべてのノードを二つに分け、それぞれで監視プロセス起動する。
それぞれのプロセスへのストリームをリストにしてPrimary, Backup
へ返す。
述語 copy(Args,Args1,Args2,Interrupt) : copy.kl1 で提供される
は、与えられた引数(変数を含む)Argsを複製し、Args1, Args2へ返す。
変数に対してはそれぞれプロセスが生成される、Argsの変数に
動的にユニファイされる値に対しては、対応するArgs1,Args2の変数に
ブロードキャストされる。 逆にArgs1, Args2へは同じ値がユニファイされる
ものとし、そのいずれかをArgsの対応する変数へ送る。
変数に対して生成されているプロセスは、
ストリーム Interruptへ fault が送られると ArgsとArgs2とをユニファイして
終了する。
[使い方]
ライブラリ watchdog.kl1、copy.kl1 および、
使用する環境に応じてncube.kl1 または sparc.kl1 、
それとユーザプログラム
(サンプルプログラムならば main.kl1 と ft_queen.kl1 )
をKLICでコンパイル、リンクして起動する。
% klic -v -dp -o dofts watchdog.kl1 copy.kl1 sparc.kl1 main.kl1 ft_queen.kl1
注)使用可能なノード数の総数が3個以上なければならない。
[実行環境]
- KLIC klic version 2.004
- nCUBE2:nCX 5.105a 3.4.0 または
- SunOS 4.1.3
[ファイル構成]
- fts/
- README.j
% 日本語版 README
- README-j.html
% 本ファイル
- README.e
% 英語版 README
- README-e.html
% 英語版 html
- watchdog.kl1
% 監視プロセス
- copy.kl1
% プロセス複製
- ncube.kl1
% nCUBE2向けライブラリ
- sparc.kl1
% SPARC向けライブラリ
- === サンプルプログラム ===
- main.kl1
% サンプルプログラム 主ルーチン
- queen.kl1
% N-Queenプログラム
- ft_queen.kl1
% 耐故障化 N-Queenプログラム
- ft_queen_faulty.kl1
% 耐故障化 N-Queenプログラム エラーあり
- main-sim.kl1
% サンプルプログラム(その2)主ルーチン
- f_sim.kl1
% サンプルプログラム(その2)
- f_sim_faulty.kl1
% サンプルプログラム(その2)エラーあり
[実行例]
分散版KLICのトレース機能が利用できないため、バグが完全に
とりきれていない。 よって現在の版では、故障に対して完全に
実行継続できない。
サンプルプログラム(その2)については、ある程度の
継続動作が見られる。
1) コンパイル
- % make sparc
2) エラー無しの実行
- % ./dofts-s -p5 10 100d
- Leader [1]
- Leader [2]
- Backup Site Group : 2 4
- Primary Site Group : 1 3
- 30 [67,1]
<== プログラム出力 A (30), B ([67,1])
- Response time is 1359 msec
3) エラーありの実行
- amethyst[300]% ./dofts-sf -p5 10 100d
- Leader [1]
- Leader [2]
- Primary Site Group : 1 3
- Backup Site Group : 2 4
- BOMB! 3
<== Node 3 でexit
- 30 FAULT was detected on PRIMARY SITE!
<== 出力 A の後、故障検出
- FAULT was informed to BACKUP!
- BACKUP change to PRIMARY !!! REBIRTH (1) [4]
- REBIRTH (2) [4]
- ... REBIRTH ndet_replay [117,1]
<== 内部メッセージの後、出力 B
- ^Ckill tasks from io_server
<== 正常終了しないため ^C
耐故障化変換 プログラムは、未完成であるので、以下に耐故障化変換の指針を記す。
[耐故障化の指針]
1) 耐故障実行させたい部分を分離する。
ユーザプログラムは、main.kl1 と queen.kl1 に分離されており、
述語 queen:queen(N,Result) を耐故障実行する。
注) オリジナルのプログラムは、main.kl1 queen.kl1 だけで動作する。
2)耐故障実行させたい部分を次の手順で変更する。
2-1) 呼び出される一番上の述語を次の例のように変更する。
- % オリジナルの述語呼び出し
- queen(N,R) :-
- % 監視プロセス生成
- watchdog:distribution(Primary,Backup),
- % 次の述語へ引数を渡す
- queen_1({N,R},Primary,Backup) @ lower_priority(10).
- % プライマリとバックアップの主プロセスへのストリームを取り
- queen_1(Args,[PTop|Primary],[BTop|Backup]) :-
- % 引数を複製し、
- copy:copy(Args,Args1,Args2,Interrupt),
- % 割り込み線をマージし、
- Interrupt = {Interrupt1,Interrupt2}, Log = ack(Log1),
- % プライマリ、バックアップそれぞれへ
- % トップのゴールをフォークする
- PTop = {primary,queen,queen,Args1,Log,Signal,Interrupt1},
- BTop = {backup ,queen,queen,Args2,Log1,Signal,Interrupt2}.
2-2) トップゴールの呼び出し口を用意する。
呼び出し口は、モジュール exgoal に定義される。
上では、モジュール queen の述語 queen は、オリジナルのアリティが2
となっているので、以下の =-=-=-=- の間の2クローズが定義される。
- :- module exgoal.
-
- call_goal(Site,Module,Predicate,Args,Log,GSig,Raise)-SC :-
- call_goal_0(Site,Module,Predicate,Args,Log,GSig,Raise)-SC @ lower_priority.
- % =-=-=-=-
- call_goal_0(primary,queen,queen,{A,B},Log,GSig,Raise)-SC :-
- queen:queen_record(A,B,Log)+GSig+Raise-SC.
- call_goal_0(backup ,queen,queen,{A,B},Log,GSig,Raise)-SC :-
- queen:queen_replay(A,B,Log)+GSig+Raise-SC.
- % =-=-=-=-
- otherwise.
- call_goal_0(Type,Module,Method,Arguments,Log,GSignal,Raise)-SC :-
- klicio:klicio([stdout(normal(Out))]),
- variable:wrap((Type::Module:Method/Arguments), G),
- Out = [fwrite("Illegal goal invocation : "),
putwt(G), nl,fflush(_)],
- Raise = [].
2-3) 2-3 で定義した トップゴール に対応する述語を用意する。
(1) もとの述語に対して Instant Replay 変換する
- % Record Version
- queen_record(N,X,Log) :-
- current_node(_,All),
- queen_0_record(N,X,~(All-1),Log)@node(1).
-
- queen_0_record(4,X,A,Log) :- queen_record([1,2,3,4],[],[],X,A,Log).
- ....
-
- queen_record([P|U],C,L,I,PE,Log) :-
- Log = c1(Log1,Log2,Log3),
- TO:= (P mod PE)+1,
- throw_record(U,[P|C],L,I2,PE,TO,Log1),
- merge_record(I1,I2,I,Log2),
- append(U,C,N),
- c1_record(P,1,N,L,L,I1,PE,Log3).
-
- % Replay Version
- queen_replay(N,X,Log) :-
- current_node(_,All),
- queen_0_replay(N,X,~(All-1),Log)@node(1).
-
- queen_0_replay(4,X,A,Log) :- queen_replay([1,2,3,4],[],[],X,A,Log).
- ....
-
- queen_replay([P|U],C,L,I,PE,Log) :-
- Log = c1(Log1,Log2,Log3) |
- TO:= (P mod PE)+1,
- throw_replay(U,[P|C],L,I2,PE,TO,Log1),
- merge_replay(I1,I2,I,Log2),
- append(U,C,N),
- c1_replay(P,1,N,L,L,I1,PE,Log3).
(2) すべてのユーザ述語について引数を追加する
引数GSigは上位からの割り込み用であり、全ゴールにブロードキャストされる
ように、サブゴールにそのまま渡す。
引数Raiseはゴールからの割り出し用なので、サブゴールが複数あるような
場合には、mergeされるようにサブゴールのRaiseストリームから成る
ベクタをユニファイする。
サブゴールが一つだけなら、そのまま渡す。 サブゴールが存在しなければ、
[] とユニファイする。
引数組 SC は、ショートサーキットするだけのためなので、そのまま各ゴール
につけるだけである。
- % Record Version
- queen_record(N,X,Log)+GSig+Raise-SC :-
- current_node(_,All),
- queen_0_record(N,X,~(All-1),Log)+GSig+Raise-SC @node(1).
-
- queen_0_record(4,X,A,Log)+GSig+Raise-SC :-
- queen_record([1,2,3,4],[],[],X,A,Log)+GSig+Raise-SC.
- ....
-
- queen_record([P|U],C,L,I,PE,Log)+GSig+Raise-SC :-
- Raise = {Raise1,Raise2,Raise3,Raise4},
- Log = c1(Log1,Log2,Log3),
- TO:= (P mod PE)+1,
- throw_record(U,[P|C],L,I2,PE,TO,Log1)+GSig+Raise1-SC,
- merge_record(I1,I2,I,Log2)+GSig+Raise2-SC,
- append_record(U,C,N)+GSig+Raise3-SC,
- c1_record(P,1,N,L,L,I1,PE,Log3)+GSig+Raise4-SC.
(3) Record Versionでは、非決定的述語で Logのack待ち部分を挿入する
- queen_record([P|U],C,L,I,PE,ack(Log))+GSig+Raise-SC :-
- Raise = {Raise1,Raise2,Raise3,Raise4},
- Log = c1(Log1,Log2,Log3),
- TO:= (P mod PE)+1,
- throw_record(U,[P|C],L,I2,PE,TO,Log1)+GSig+Raise1-SC,
- merge_record(I1,I2,I,Log2)+GSig+Raise2-SC,
- append_record(U,C,N)+GSig+Raise3-SC,
- c1_record(P,1,N,L,L,I1,PE,Log3)+GSig+Raise4-SC.
- queen_record([],[_|_],_,I,ack(Log))+Sig+Raise-SC:-
- Raise=[],
- Log=c2(Ack),
- I=[].
(4) Record Version では、非決定的述語で ボディユニファイのところに
ack待ちを挿入する (出力コミット)
- queen_record([],[_|_],_,I,ack(Log))+Sig+Raise-SC:-
- Raise=[],
- Log=c2(Ack),
- output(Ack,I, [])-SC.
-
- output(Ack,X,Y)-SC :- wait(Ack) | X = Y.
(5) Replay Version では各述語の前に 割り込みチェック処理用の
クローズを挿入する
- % Replay Version
- queen_replay(A, B, Log)+GSig+Raise-SC :-
- (wait(Log) -> queen_replay_0(A, B, Log)+GSig+Raise-SC ;
- alternatively;
- GSig = [rebirth|GSig1] -> queen_record(A, B, _)+GSig1+Raise-SC).
- queen_replay_0(N,X,Log)+GSig+Raise-SC :-
- current_node(_,All),
- queen_0_replay(N,X,~(All-1),Log)+GSig+Raise-SC @node(1).
-
- queen_0_replay( 4,X,A,Log)+GSig+Raise-SC :-
- queen_replay([1,2,3,4],[],[],X,A,Log)+GSig+Raise-SC.
- ....
-
- queen_record([P|U],C,L,I,PE,Log)+GSig+Raise-SC :-
- Log = c1(Log1,Log2,Log3) |
- Raise = {Raise1,Raise2,Raise3,Raise4},
- TO:= (P mod PE)+1,
- throw_replay(U,[P|C],L,I2,PE,TO,Log1)+GSig+Raise1-SC,
- merge_replay(I1,I2,I,Log2)+GSig+Raise2-SC,
- append_replay(U,C,N)+GSig+Raise3-SC,
- c1_replay(P,1,N,L,L,I1,PE,Log3)+GSig+Raise4-SC.
- queen_replay([],[_|_],_,I,ack(Log))+Sig+Raise-SC:-
- Log=c2(Ack) |
- Raise=[],
- I=[].
(6) throw goal ('goal @ node(N)')は、すべて次のような割り出しに変える
- throw_record(A,B,C,D,E,F,Log)+GSig+Raise-SC :-
- Raise = [goal(primary,queen,queen,{A,B,C,D,E},Log)].
- throw_replay(A,B,C,D,E,F,Log)+GSig+Raise-SC :-
- Raise = [goal(primary,queen,queen,{A,B,C,D,E},Log)].
現在のバージョンでは、隣のノードへ勝手に投げるのでノード番号は
必要ない。
(7) throw goalするゴールに対応する入口を モジュールexgoalに追加する。
- call_goal_0(primary,queen,queen,{A,B,C,D,E},Log,GSig,Raise)-SC :-
- queen:queen_record(A,B,C,D,E,Log)+GSig+Raise-SC.
- call_goal_0(backup ,queen,queen,{A,B,C,D,E},Log,GSig,Raise)-SC :-
- queen:queen_replay(A,B,C,D,E,Log)+GSig+Raise-SC.
- ....
- otherwise.
- call_goal_0(Type,Module,Method,Arguments,Log,GSignal,Raise)-SC :-
- klicio:klicio([stdout(normal(Out))]),
- variable:wrap((Type::Module:Method/Arguments), G),
- Out = [fwrite("Illegal goal invocation : "),
putwt(G), nl,fflush(_)],
- Raise = [].
注)ft_queen.kl1 は、throw goalするゴールをc1に変え、
その他不必要な部分をオプティマイズしたものである。
本文章について
- 1997年4月 記述 by Eiji Sugino
- 1997年5月21日 html化
sugino@jaist.ac.jp