マルチスレッドとキャッシュとメンバ変数
id:motom552:20101031
上の記事の追記に近い覚書
マルチスレッド開発はメモリを愛する事から始める
常にメモリの事を考えながら開発しないと、すぐにパフォーマンスに影響出る事が分かった。
処理内容
単純に0から1<<31までのループ値を加算し総合値を取得する。(0+1+2〜)
シングルスレッド
まあ書く意味あるのかってくらい単純。
rn_uint32 total = 0; { const rn_uint32 endIdx = loop;// loopは(1<<31) for( rn_uint32 i=0; i<endIdx; ++i){ total += i; } }
マルチスレッド
こっちはスレッドクラスを使います。
作成中のクラスを継承してますが、スレッド側からExcute()が呼ばれる事には変わりないです。
下記のようなスレッドクラスを用意しインスタンスを4つ用意する。
インスタンスごとにループ領域を分ける。
class Job : public RinThreadJob { rn_uint32 m_sum; rn_uint32 m_beginIdx; rn_uint32 m_endIdx; // rn_uint8 m_pad[1<<8]; public: Job():m_sum(0),m_beginIdx(0),m_endIdx(0){} void Execute() { rn_uint32 total = 0; const rn_uint32 endIdx = m_endIdx; for( rn_uint32 i=m_beginIdx; i<endIdx; ++i){ m_sum += i; // total += i; } // m_sum = total; } rn_uint32 GetSum(void)const{ return m_sum; } void SetRange( const rn_uint32 begin, const rn_uint32 end ){ m_beginIdx = begin; m_endIdx = end; } };
コメントアウトは後述。
メイン関数部分は下記のように配列で4つ用意します。
void main() { Job job[4]; }
結果
実行タイプ | 最小[ms] | 最大[ms] |
---|---|---|
シングルスレッド | 6606 | 6624 |
マルチスレッド | 4101 | 6959 |
まあこれぐらいの速度差ならマルチにする価値はないわけで。
理由
そして理由は最初に紹介している記事と同じ理由で、キャッシュの再取得が発生しているため。
メイン関数で配列4つ宣言するということは、メモリが連続して配置されます。
これを各スレッドがアクセスすると、各キャッシュが重複します。
そしてその重複しているメモリつまり、メンバ変数に各スレッドがアクセスしまくります。
※クラスがどのような形でメモリに乗っかるかはid:motom552:20090109を参照してください。
対応
このケースでの対応は2パターンありますが、どちらもスレッド間のアクセス重複を防ぐ方法。
- 加算処理等をスタック変数に結果だけ渡す。
- 重複しないくらいメモリ間を離す。つまりクラスのサイズをでかくする。
加算処理等をスタック変数に結果だけ渡す。
上のソースの下二つのコメントアウトのようにアクセスが多いループ内をメンバ変数ではなく、
スタック変数にし、結果だけを受け取るようにします。
再度測定
実行タイプ | 最小[ms] | 最大[ms] |
---|---|---|
シングルスレッド | 6606 | 6624 |
マルチ(パッドなし+メンバ直使用) | 4101 | 6959 |
マルチ(パッドなし+スタック使用) | 1222 | 1247 |
マルチ(パッドあり+メンバ直使用) | 1693 | 1711 |
マルチ(パッドあり+スタック使用) | 1222 | 1251 |
だいぶ価値あるものになったかと思います。
パッドなし+スタック使用がパッドあり+スタック使用と同じなのは、
スタック変数はスレッドごとのメモリにあるので、重複しません。
結論
序盤でも述べているように常にメモリの事を考えておかないと簡単にマルチスレッドの価値を失います。
おまけにキャッシュの状態なんて分からないので、パフォーマンス測定結果をただ見るだけでそれがキャッシュの非効率なのか単純に重いのかは判断難しいと想像。ここらへんは実際に試してみないと分かりませんが。。
知らずに非効率なマルチスレッドになって開発だけはし辛いという・・なんとも無様な。。
最後に
上記記事はあくまで自分で仮説、測定、考察した結果なので、そもそも間違いがある場合があります。
特にキャッシュ周りとかは記事読んだだけなので。
ただ測定結果を見る限り多分そこまで間違いはないかと。。