並列化後のプログラムで発生する不具合現象はこのようなものです。
ABCDEF00 = C350 * E130
ABCDEF00 = EA60 * BBA8
ABCDEF00 = EA92 * BB80
ABCDEF00 = BB80 * EA92
ABCDEF00 = BBA8 * EA60
この現象はまれに発生します。
ほとんどの場合、正しく動いてくれますが、感覚的に10回に1回くらいでしょうか。このように、おかしな結果を表示します。
現象は、先にお見せした逐次処理に比べて表示行数が1行足りません。
この不具合の発生はまれなので、2度・3度くらいの実行では見つかりません。
今回は平均的な動作時間を見るために何度も動かしましたから発見できましたが、普通なら見逃したはずです。
この現象の原因ですが、それはリソース競合でした。
#pragma omp parallel for
によりスレッド分割されて動作する子関数の sub(i); の中で静的変数を使用しています。
複数スレッドが同じ変数(アドレス)へアクセスすることになりますが、排他処理がありません。
このために、運悪く複数スレッドが同時にアクセスにすると想定外の動作が起こります。
例えば、書いたはずの値とは違う値を読んでしまう等の現象です。
EMC組み込みマルチコアサミット2020、セミナー資料より
競合する変数としない変数の違いは何でしょうか。
実際の sub関数を見ると、関数には引数 i とオート変数 j があります。
これらはローカル変数(スレッドローカルではありません)と呼ばれ、スタックエリアやレジスタに割り当てられます。この領域はそれぞれのスレッド毎(関数毎)に別々に確保されますので、スレッド間で競合することはありません。
これに対して赤色表示の static変数はプログラム全体で1つしかありません。1つの変数に複数のスレッドがアクセスすることになり、まれに同時アクセスが発生します。
競合する変数へのアクセスを行う際には占有できる状態でアクセスしなければならないのですが、sub関数やsub関数の子関数内の変数アクセス処理部には排他制御処理がありません。
EMC組み込みマルチコアサミット2020、セミナー資料より
次回は、このプログラムの不具合対策について説明します。
コメントをお書きください