基本情報技術者 2014年 春期 午後 問03
プログラムの並列実行に関する次の記述を読んで、設問1〜3に答えよ。
プログラムを並列に実行する方法として、スレッドを使用した並列実行がある。スレッドを使用した並列実行では、プログラムの中で並列実行が可能な部分を抽出して複数の処理に分割し、分割した処理を別々のスレッドとして同時に実行する。
スレッドによる並列実行の例を、図1に示す。プログラムAは、一つのプロセスとして実行され、データ作成、計算処理、結果出力の順に処理が行われる。ここで、計算処理はn個の処理に分割して並列実行が可能であり、分割した処理を異なるスレッドで並列に実行した結果は、スレッドを使わずに計算処理した実行結果と同じであるとする。計算処理では、スレッドの生成と実行を行い、全てのスレッドの終了を同期処理で待つ。各スレッドでは、分割した計算処理(部分計算処理)を行う。
プログラムAを実行するコンピュータの構成は、性能が同じである複数のCPUと各CPUからアクセス可能な共有メモリで構成されているマルチプロセッサとする。生成された複数のスレッドは、異なるCPUに割り当てられて同時に実行され、各スレッドは共有メモリでデータを共有する。マルチプロセッサにおけるスレッドとCPU、共有メモリの関係を、図2に示す。ここで、データを転送するバスの競合は無いものとする。

設問1:
問題文を見る 次の記述中のに入れる正しい答えを、解答群の中から選べ。
マルチプロセッサによる並列実行で得られる理想的な高速化率Eは、次の式で求められる。
n:CPUの数(n ≧ 1)
r:対象とする処理のうち、並列実行が可能な部分の処理時間の割合(0 ≦ r ≦ 1)
図1のプログラムAにおいて、データ作成、計算処理、結果出力の処理時間の割合が7:90:3の場合、単一のCPUで実行したときと比べた高速化率を5以上にするには、CPUが最低a個必要である。ここで、スレッドの生成処理などの並列実行に伴うオーバヘッドは考慮しない。
aに関する解答群
ア:5
イ:6
ウ:7
エ:8
オ:9
カ:10
模範解答
a:オ
解説
解答の導き方
求めるのは、単一のCPUで実行した場合と比べて高速化率を5以上にするために必要なCPUの最小数です。CPU(Central Processing Unit:プログラムの命令を処理する装置)の数を、問題文の式の に当てはめます。
まず、処理時間の割合「7:90:3」は、データ作成、計算処理、結果出力の順です。合計は なので、計算処理にかかる時間は全体の です。本文には「計算処理はn個の処理に分割して並列実行が可能」とあります。したがって、並列実行が可能な部分の割合 は です。
一方、データ作成と結果出力は並列実行できる部分として示されていません。合わせて全体の なので、式の に当たります。CPUを増やしても、この部分の処理時間は短くなりません。
高速化率の式に を入れると、次のようになります。
高速化率を5以上にする条件は、次のとおりです。
分母は正なので、両辺を整理すると、
よって、
となります。実際、 のときは、
です。CPUは整数個なので、高速化率を5以上にする最小の数は9個です。
誤りやすいポイント
- に、並列実行できる計算処理の割合である ではなく、並列実行できない部分の割合 を入れてしまうことがあります。式の説明では、 は「並列実行が可能な部分」の割合です。
- データ作成、計算処理、結果出力の割合を取り違えないようにします。計算処理の割合が です。
- CPUを増やすと全体の処理時間がCPU数に反比例して短くなる、と考えるのは誤りです。短縮されるのは並列実行できる部分だけで、並列実行できない は残ります。
- 「5以上」なので、高速化率がちょうど5になる場合も条件を満たします。 で高速化率がちょうど5になります。
FAQ
Q: CPUを増やし続ければ、高速化率も際限なく大きくなりますか?
A: いいえ。並列実行できない部分の処理時間は残ります。この問題では全体の が残るため、CPUを非常に多くしても高速化率は最大で10に近づきます。
A: いいえ。並列実行できない部分の処理時間は残ります。この問題では全体の が残るため、CPUを非常に多くしても高速化率は最大で10に近づきます。
Q: スレッドとは何ですか?
A: スレッドは、プログラムの処理を分けて実行する単位です。本文では、計算処理を複数に分け、それぞれを別のスレッドで同時に実行しています。
A: スレッドは、プログラムの処理を分けて実行する単位です。本文では、計算処理を複数に分け、それぞれを別のスレッドで同時に実行しています。
Q: この計算で、スレッドの生成にかかる時間を含めないのはなぜですか?
A: 問題文で、スレッドの生成処理などの並列実行に伴うオーバヘッド(本来の計算以外にかかる追加の時間)を考慮しないと指定されているためです。
A: 問題文で、スレッドの生成処理などの並列実行に伴うオーバヘッド(本来の計算以外にかかる追加の時間)を考慮しないと指定されているためです。
関連キーワード: 並列処理、高速化率、CPU、スレッド、逐次処理
設問2:
問題文を見る 次の記述中のに入れる正しい答えを、解答群の中から選べ。
プログラムの一部を複数の処理に分割して並列に実行するためには、プログラムの中から並列実行が可能な部分を抽出する必要がある。並列実行が可能なループの例を、図3に示す。図3は、iのループに関してループを四つに分割し、分割したそれぞれのループの処理をスレッドとして並列実行する場合である。ここで、配列のデータはスレッド間で共有され、変数iはスレッドごとに確保されるものとする。

プログラムの中から並列実行が可能な部分を抽出する場合、並列に実行してもデータの更新と参照の順序が変化しないことを保証する必要がある。図4に示すプログラム1〜3を、iのループに関して複数のループに分割し、分割したそれぞれのループの処理を並列に実行する場合の並列実行可能性について考える。ここで、配列は十分に大きいものとする。
プログラム1は、ループの中でb、並列実行できない。プログラム2は、ループの中でc、並列実行できない。プログラム3は、mの値が不明の場合には並列実行できないが、dであることが保証されていれば並列実行は可能である。

b, cに関する解答群
ア:更新した値が次の繰返しで参照されるので
イ:更新した値が次の繰返しで再び更新されるので
ウ:参照した値が次の繰返しで更新されるので
エ:参照した値が次の繰返しで再び参照されるので
dに関する解答群
ア:m ≧ 0
イ:m ≧ n
ウ:m ≦ 0
エ:m ≦ n
模範解答
b:ア、c:ウ、d:イ
解説
解答の導き方
並列実行とは、複数の処理を同時に進めることです。ループ(同じ処理を繰り返す構造)を並列実行するには、ある繰返しの結果を別の繰返しが必要としないかを確かめます。
問題文には、並列に実行しても「データの更新と参照の順序が変化しないこと」を保証する必要があるとあります。配列(複数の値を番号で管理する入れ物)はスレッド(プログラム内で処理を進める実行単位)の間で共有されるため、ある繰返しが書き換えた値を、別の繰返しが読む場合は注意が必要です。一方、変数 はスレッドごとに確保されます。そのため、ここでは ではなく、配列 の値の読み書きに注目します。
プログラム1
ループは から始まり、各繰返しで を読み、 に書き込みます。
の繰返しで更新した を、次の の繰返しが読み取ります。したがって、 の処理が終わる前に の処理を進めると、必要な値を正しい順序で読めるとは限りません。
これは、選択肢の「更新した値が次の繰返しで参照される」に当たるため、bはアです。
プログラム2
ループは から始まり、各繰返しで を読み、 に書き込みます。
の繰返しが読む は、次の の繰返しで更新されます。順番に実行すれば、 は更新前の を読みます。しかし、並列実行では が先に を更新し、 がその更新後の値を読んでしまう可能性があります。
つまり、「参照した値が次の繰返しで更新される」ため、cはウです。
プログラム3
各繰返しは を読み、 に書き込みます。ループ全体では、書き込み先は から です。
であれば、どの繰返しでも読み取り先の添字は次の範囲になります。
つまり、読み取り先はすべて 以降です。ループが書き込む から とは重ならないため、ある繰返しが書いた値を別の繰返しが読むことはありません。したがって、選択肢の中では が並列実行を可能にする条件です。dはイです。
誤りやすいポイント
-
プログラム1とプログラム2の依存関係を逆にすること
プログラム1では、先の繰返しが更新した値を次の繰返しが読みます。プログラム2では、先の繰返しが読んだ値を次の繰返しが更新します。どちらも順序の影響を受けますが、読み書きの順番が逆です。 -
がスレッドごとに分かれていれば、並列実行できると思うこと
が別々でも、共有されている配列の要素を複数の繰返しが読み書きすれば、値を扱う順序が変わる可能性があります。 -
なら安全だと思うこと
例えば では、繰返し が読む を、繰返し が更新します。単に が0以上というだけでは、読み取り先と書き込み先が重ならないとは限りません。
FAQ
Q: 配列の値を読むだけでも、並列実行の問題になりますか?
A: 読むだけなら、別の繰返しがその値を書き換えない限り、順序の問題は起きません。プログラム2のように、ある繰返しが読んだ値を別の繰返しが更新する場合は、どちらが先に実行されるかで読み取る値が変わる可能性があります。
A: 読むだけなら、別の繰返しがその値を書き換えない限り、順序の問題は起きません。プログラム2のように、ある繰返しが読んだ値を別の繰返しが更新する場合は、どちらが先に実行されるかで読み取る値が変わる可能性があります。
Q: プログラム3で、読み取り先が書き込み先と重ならないことが重要なのはなぜですか?
A: 読み取り先と書き込み先が重ならなければ、別の繰返しによる更新が読み取り値に影響しません。そのため、繰返しの実行順序を変えても結果が変わりません。
A: 読み取り先と書き込み先が重ならなければ、別の繰返しによる更新が読み取り値に影響しません。そのため、繰返しの実行順序を変えても結果が変わりません。
Q: 「配列は十分に大きい」とあるのは何のためですか?
A: 配列の端を超えて読み書きする問題を考えなくてよいようにするためです。この設問では、配列の大きさではなく、読み取り先と書き込み先が重なるかどうかを判断します。
A: 配列の端を超えて読み書きする問題を考えなくてよいようにするためです。この設問では、配列の大きさではなく、読み取り先と書き込み先が重なるかどうかを判断します。
関連キーワード: 並列処理、スレッド、ループ依存、データ依存、共有メモリ
設問3:
問題文を見る 図5に示すプログラム4は、配列aで更新する要素を示すインデックスの値が配列ipで間接的に決定される。この配列aのような更新を含むプログラムは、配列ipの値によっては並列実行できない場合があるので注意が必要である。プログラム4を、図5のようにiのループに関して複数のループに分割し、分割したそれぞれのループの処理をスレッドで並列実行するとき、並列実行可能なip[i](i = 1, 2, …, 20)の値として適切な答えを、解答群の中から選べ。

解答群


模範解答
イ
解説
解答の導き方
プログラム4では、各回の処理で「」を行います。つまり、配列のどの要素を書き換えるかは、配列の値で決まります。
図5では、ループを4つのスレッドに分けています。スレッド1は、スレッド2は、スレッド3は、スレッド4はを担当します。スレッドは別々の処理を同時に進めるため、異なるスレッドが同じの要素を書き換えると、どちらの値が最後に残るかが不確かになります。このような問題をデータ競合といいます。
したがって、確認するのは、別々のスレッドが同じ添字のを書き換えないかどうかです。たとえば、でなら、その回で更新するのはです。
選択肢イの値を、スレッドごとに分けて確認します。
どのスレッドの組にも、同じ添字がありません。つまり、スレッドどうしが同じの要素を書き換えることはなく、並列実行できます。
ほかの選択肢では、異なるスレッドが同じ要素を更新します。
- アでは、スレッド1のとスレッド2のが、どちらもを更新します。
- ウでは、スレッド1のとスレッド2のが、どちらもを更新します。
- エでは、スレッド1のとスレッド4のが、どちらもを更新します。
本文に「配列の値によっては並列実行できない場合がある」とあるのは、このように更新先が重なる場合があるためです。別々のスレッドによる更新先が重ならない選択肢はイです。
誤りやすいポイント
- の値が重ならないように分割されていることだけでは、並列実行できるとは限りません。確認すべきなのは、実際に更新するの添字です。
- の値が異なるスレッド間で重なると、同じの要素への書込みが同時に起こる可能性があります。
- この問題では、の値が全て異なるかを確認すれば判断できます。ただし、考え方として重要なのは、異なるスレッドどうしで更新先が重ならないことです。
FAQ
Q: なぜの範囲が分かれていても、更新先が重なるのですか?
A: は、ループのどの回を実行するかを表します。一方、実際に書き換えるの位置はで決まります。異なるでも、が同じなら同じの要素が更新されます。
A: は、ループのどの回を実行するかを表します。一方、実際に書き換えるの位置はで決まります。異なるでも、が同じなら同じの要素が更新されます。
Q: 同じスレッド内で更新先が重なる場合も、必ず問題になりますか?
A: この問題で注意するのは、異なるスレッドが同じ要素を書き換えることです。同じスレッド内の処理は順番に実行されるため、別スレッド間での同時書込みとは状況が異なります。
A: この問題で注意するのは、異なるスレッドが同じ要素を書き換えることです。同じスレッド内の処理は順番に実行されるため、別スレッド間での同時書込みとは状況が異なります。
Q: の値も確認する必要がありますか?
A: この処理では、を読み、に書き込みます。更新先が別々であれば、読み取るの値が異なること自体は問題になりません。
A: この処理では、を読み、に書き込みます。更新先が別々であれば、読み取るの値が異なること自体は問題になりません。
関連キーワード: 並列処理、スレッド、共有メモリ、データ競合、配列索引




