620 likes | 736 Views
Chapter4.9-4.12. 6311669 藤田 晋哉 @M1 in 武田研. 入力ストリームの利用. 入力ストリーム上で二つの操作が行われる ストリームの読み込み ストリームの EOS チェック. 入力ストリームの利用. ストリームの EOS チェック ストリームの先頭の要素をチェック →ストリームの終わりであるなら T rue →終わりでないならストリームが開いているのを示しながら False を返す. 入力ストリームの利用. ストリーム 要素の読み込み 読むべきストリームが空であるなら次のストリームの要素 を呼び出す .
E N D
Chapter4.9-4.12 6311669 藤田晋哉 @M1in武田研
入力ストリームの利用 • 入力ストリーム上で二つの操作が行われる • ストリームの読み込み • ストリームのEOSチェック
入力ストリームの利用 • ストリームのEOSチェック • ストリームの先頭の要素をチェック →ストリームの終わりであるならTrue →終わりでないならストリームが開いているのを示しながらFalseを返す.
入力ストリームの利用 • ストリーム要素の読み込み • 読むべきストリームが空であるなら次のストリームの要素を呼び出す. • 閉じられたストリーム上でリードオペレーションを実行した場合はtrueを返す. -> 返り値を用いてループ記述のが一般的 while(co_stream_read(input_stream) == co_err_none) { //ループしたい処理 }
入力ストリームの利用 • ストリームを閉じる(co_stream_close) • 入力ストリームがco_stream_closeで閉じられたとき、それ以降のデータは破棄されてEOSトークンが消費される. • EOSがストリーム上にない場合はco_stream_closeはブロックされる. ライタが意図しない終了を防ぐ. (読む側からはEOSトークンを検出するまでは ストリーム を閉じない)
EOS検出 • Eosを検出する(co_stream_eos) • ライターがストリームをとじたらco_stream_eosはTrueを返す. • 一回Falseを返したら,リーダがストリームを閉じるまではその後で呼び出されたco_stream_eosはFalseを返す. • co_stream_eosはノンブロッキングなのでそれ以降もストリームへのアクセスは可能. • co_stream_readの呼び出しもそれ以降は co_err_eosを返す. (↑これをループ記述に使える)
ストリームの効率的な利用 • 効率的にストリームを利用するには ImpulseCを使ってかっこいいプログラミング をすることが重要.
ストリームの効率的な利用 • パターン1(残念な例) while(!co_stream_eos(input_stream)) { co_stream_read(input_stream,&i,sizeof(i) ); …...// 処理の内容 } {EOSはないか?→co_stream_read→処理} この構成はあまり良くない
ストリームの効率的な利用 • パターン1(残念な例) while(!co_stream_eos(input_stream)) { co_stream_read(input_stream,&i,sizeof(i) ); …...// 処理の内容 } co_stream_eosとco_stream_readの間で送り手からストリームが閉じられるかも. →リードバッファに有効な値が格納されない. この間でEOSが呼び出されたらまずい
ストリームの効率的な利用 • パターン1(残念な例) while(!co_stream_eos(input_stream)) { co_stream_read(input_stream,&i,sizeof(i) ); ……// 処理の内容 } この部分で値をチェックすればいいね
ストリームの効率的な利用 • パターン2(大丈夫な記述) while(!co_stream_eos(input_stream)){ if(co_stream_read(input_stream, &i, sizeof(i)) ==co_err_none){ ……//処理の内容 } } co_stream_readで値のチェックを行うようにした. とりあえず問題のない記述.けど冗長
ストリームの効率的な利用 • パターン3(好ましい書き方) if ( co_stream_read(input_stream, &i, sizeof(i)) == co_err_none ) { do{ ……// 処理の内容 } while( co_stream_read(input_stream, &i, sizeof(i)) == co_err_none); } 処理サイクルが早くなるパターン
ストリームの効率的な利用 if ( co_stream_read(input_stream, &i, sizeof(i)) == co_err_none ) { do{ // 処理の内容 } while( co_stream_read(input_stream, &i, sizeof(i)) == co_err_none); } while(!co_stream_eos(input_stream)){ if(co_stream_read(input_stream, &i, sizeof(i)) ==co_err_none){ //処理の内容 } } ReadのチェックがWhile内に含まれないので早くなる.
ストリームの効率的な利用 • パターン4(好ましい記述) while(co_stream_read(input_stream) == co_err_none){ …… //処理の内容 } co_stream_eosを明示的に呼び出すのを除去した形.
ストリームの効率的な利用 while(co_stream_read(input_stream) == co_err_none){ //処理の内容 } while(!co_stream_eos(input_stream)){ if(co_stream_read(input_stream, &i, sizeof(i)) ==co_err_none){ //処理の内容 } } 集積回路の面積がコンパクトですむ.
ストリームデッドロックの回避 • ストリームのデッドロックは他のプロセスがタスクを終えて、データを出力に書き込むまで一つのプロセスが進まない時に起きる • 不規則なデータを扱うシステムやサイクルの遅延が変化しやすいシステムでは厄介な 問題. • バッファの深さを増やすと解決したかのように見えることがあるけど実際はバッファの深さで解決するケースはすくない
ストリームデッドロックの回避 • デッドロックの例 四つの値を逆順にして返す Supervisor – 四つの値をストリームS1に送る. 逆順になって帰ってくる四つの値 をS2からread. Worker –S1からreadした値をS2に逆順でwrite (LIFO)
ストリームデッドロックの回避 • Supervisor for ( i = 0; i < iterations; i++ ) { for ( j = 0; j < 4; j++ ) { local_S1 = rand(); printf("S1 = %d\n", local_S1); co_stream_write(S1, &local_S1, sizeof(uint32)); //S1に四つの値をwrite } for ( j = 0; j < 4; j++ ) { co_stream_read(S2, &local_S2, sizeof(uint32)); //S2から四つの値をread printf("S2 = %d\n", locai_S2); }} ランダムに値を選ぶ(&出力)→S1にwrite →S2をread
ストリームデッドロックの回避 • Worker while (!co_stream_eos(S1)) { for (i = 0; i < 4; i++) co_stream_read(S1, &data[i], sizeof(uint32)); //S1から四つread for (i = 0; i < 4; i++) co_stream_write(S2, &data[3-i], sizeof(uint32));//S2に四つwrite } Workerプロセスは S1の四つの値をread→S2に四つの値をwrite
ストリームデッドロックの回避 Supervisor local_S1 = rand(); 1 2 3 4 S1 S2
ストリームデッドロックの回避 Supervisor co_stream_write(S1, &local_S1,sizeof(uint32)); S1 S2 4 3 2 1
ストリームデッドロックの回避 Worker co_stream_read(S1, &data[i],sizeof(uint32)); S1 S2 1 2 3 4
ストリームデッドロックの回避 Worker co_stream_write(S2, &date[3-i],sizeof(uint32)); S1 S2 1 2 3 4 後ろの要素から
ストリームデッドロックの回避 Worker co_stream_write(S2, &date[3-i],sizeof(uint32)); S1 S2 4 3 2 1
ストリームデッドロックの回避 Supervisor co_stream_read(S2, &local_S2,sizeof(uint32)); 4 3 2 1 S1 S2
ストリームデッドロックの回避 • SuperVisor:パケット長に依存しないように SuperVisor for ( i = 0; i < iterations *4; i++ ) { local_S1 =rand(); printf("S1 = %d\n", locai_S1); co_stream_write(S1, &locai_S1, sizeof(uint32)); co_stream_read(S2, &locai_S2, sizeof(uint32)); //デッドロック printf("S2 = %d\n", locai_S2); } この場合はデッドロックする.
ストリームデッドロックの回避 Supervisor local_S1 = rand(); 1 S1 S2
ストリームデッドロックの回避 Supervisior co_stream_write(S1, &local_S1,sizeof(uint32)); S1 S2 1
ストリームデッドロックの回避 Worker co_stream_read(S1, &data[i],sizeof(uint32)); S1 S2 1
ストリームデッドロックの回避 • Worker while (!co_stream_eos(S1)) { for (i = 0; i < 4; i++) co_stream_read(S1, &data[i], sizeof(uint32)); //S1から四つread for (i = 0; i < 4; i++) co_stream_write(S2, &data[3-i], sizeof(uint32));//S2に四つwrite } Workerは四つreadするまでは進まない
ストリームデッドロックの回避 SuperVisor for ( i = 0; i < iterations *4; i++ ) { local_S1 =rand(); printf("S1 = %d\n", locai_S1); co_stream_write(S1, &locai_S1, sizeof(uint32)); co_stream_read(S2, &locai_S2, sizeof(uint32)); //デッドロック printf("S2 = %d\n", locai_S2); }
ストリームデッドロックの回避 Worker→S1から次の値がくるのを待ってる SuperVisor→S2に値がくるのを待ってる -> デッドロック S1 S2 1
ストリームデッドロックの回避 • ストリームを用いる際は設計に注意 • プロセスの問題とストリームの同期を常に考慮しておかないといけない. • 大きな再設計なしで解決する場合もある.
NonBlockingなストリーム読み込み • SuperVisor側は通常,パケットの長さに依存するべきではない. • 任意の長さの出力を求められるのが普通. →ブロックされないストリーム読み込み co_stream_read_nbを使うと捗る.
NonBlockingなストリーム読み込み SuperVisor for ( i = 0; i < iterations *4; i++ ) { local_S1 =rand(); printf("S1 = %d\n", locai_S1); co_stream_write(S1, &locai_S1, sizeof(uint32)); while(co_stream_read_nb(S2, &locai_S2, sizeof(uint32))) printf("S2 = %d\n”, locai_S2); } S2の読み込みをブロックしないようにしておく.
NonBlockingなストリーム読み込み Worker→S1から次の値がくるのを待ってる SuperVisor→S2に値がないので次へ S1 S2 1
NonBlockingなストリーム読み込み Supervisior co_stream_write(S1, &local_S1,sizeof(uint32));に戻る S1 S2 2 1
NonBlockingなストリーム読み込み Worker co_stream_write(S2, &date[3-i],sizeof(uint32)); S1 S2 1 2 3 4
NonBlockingなストリーム読み込み Worker co_stream_write(S2, &date[3-i],sizeof(uint32)); S1 S2 4 3 2 1
NonBlockingなストリーム読み込み Supervisior while (co_stream_read_nb(S2, &locai_S2, sizeof(uint32))) S2が空になるまで読み込み 4 3 2 1 S1 S2
NonBlockingなストリーム読み込み • データ長に依存しないようなループを記述する場合は,co_stream_read_nbを用いると解決することが多い. • ポーリングしていくことになるので大抵の場合で速度は落ちる
デッドロックとPIPELINE • PIPELINEを使うとデッドロックの状態をシミュレートすることができる. • 並列化の時にPIPELINEによる複数の繰返しを使う事が多い. →PIPELINEについてはもっと後に詳細 (別のChapter)
シグナルの作成と利用 • インパルスCはプロセスとストリームの間のデータ移動を少なくすることが特徴の一つ. • ストリームを用いてなるべく少ない数の入力と出力でプロセス間の同期をとることができる.
シグナルの作成と利用 • 同期をとる方法の一つとしてシグナル. • シグナルでメッセージパッシングが可能. シグナルread→co_signal_wait(ブロックする) シグナルwrite→co_signal_post(ノンブロッキング) • 送るプロセスはco_signal_postを呼び出して受け取るプロセスはco_signal_waitを呼び出す
シグナルの作成と理解 一つの入力ストリームで同期を行う例 do { for (i =0; i < 8; i++) { if(co_stream_read(stream_input, &data, sizeof(uint32)) ==co_err_none) co_stream_write(stream_output, &data, sizeof(uint32)); else printf("Unexpected end of data streamdetected in proc1.\n"); } co_signal_post(ready_signal_output, i); co_signal_wait(ack_signal_input); } while (co_stream_eos(stream_input) ==co_err_none);
シグナルの作成と理解 8個の値をstream_inputから読み込んでstream_outputに流す.(これ自体に多分深い意味は無い) do { for (i =0; i < 8; i++) { if(co_stream_read(stream_input, &data, sizeof(uint32)) ==co_err_none) co_stream_write(stream_output, &data, sizeof(uint32)); else printf("Unexpected end of data streamdetected in proc1.\n"); } co_signal_post(ready_signal_output, i); co_signal_wait(ack_signal_input); } while (co_stream_eos(stream_input) ==co_err_none);
シグナルの作成と理解 co_signal_postでメッセージのポスト(8個の値の出力が終わった事を知らせる) do { for (i =0; i < 8; i++) { if(co_stream_read(stream_input, &data, sizeof(uint32)) ==co_err_none) co_stream_write(stream_output, &data, sizeof(uint32)); else printf("Unexpected end of data streamdetected in proc1.\n"); } co_signal_post(ready_signal_output, i); co_signal_wait(ack_signal_input); } while (co_stream_eos(stream_input) ==co_err_none);
シグナルの作成と理解 co_signal_waitで他のプロセスからのメッセージを待つ. (メッセージが来たら次の値をセット) do { for (i =0; i < 8; i++) { if(co_stream_read(stream_input, &data, sizeof(uint32)) ==co_err_none) co_stream_write(stream_output, &data, sizeof(uint32)); else printf("Unexpected end of data streamdetected in proc1.\n"); } co_signal_post(ready_signal_output, i); co_signal_wait(ack_signal_input); } while (co_stream_eos(stream_input) ==co_err_none);
シグナルの作成と理解 • co_signal_waitはブロックされる. →メッセージを受け取るまではプロセスを続けることはできない. • co_signal_postはノンブロック. →メッセージが受け取られるまで待ったりはしない.(メッセージが存在するときにpostしなおすとメッセージの値は上書きされる.)