2016-12-04 8 views
0

私は、アプリケーションのいくつかの機能をどれくらい時間がかかるか見るために計測しようとしています。私はリンクされたリストを使ってすべての時間をメモリに記録しています。OMP領域でのロック

このプロセスでは、リストの最後を追跡するグローバル変数を導入しました。新しいタイミング領域に入ると、リストの最後に新しいレコードが挿入されます。かなり簡単なもの。

しかし、私が追跡したい機能の一部は、OpenMP領域で呼び出されます。これは、複数回並行して呼び出される可能性が高いことを意味します。そして、これは私が困惑している場所です。

これは通常のPthreadsを使用していた場合、私はミューテックスのグローバル変数へのアクセスをラップし、それを1日と呼びます。しかし、私は確信しています:この戦略はまだOpenMP領域で呼び出される関数で動作しますか?で、彼らはロックを尊重しますか?例えば

(コンパイルが、私は全体のポイントを取得し、考えていません):

私は、その後に更新してしまう
Record *head; 
Record *tail; 

void start_timing(char *name) { 
    Record *r = create_record(name); 
    tail->next_record = r; 
    tail = r; 
    return r; 
} 

int foo(void) { 
    Record r = start_timing("foo"); 
    //Do something... 
    stop_timing(r); 
} 

int main(void) { 
    Record r = start_timing("main"); 
    //Do something... 
    #pragma omp parallel for... 
    for (int i = 0; i < 42; i++) { 
     foo(); 
    } 
    //Do some more... 
    stop_timing(r); 
} 

:これは明白な答えを持っている場合

void start_timing(char *name) { 
    Record *r = create_record(name); 

    acquire_mutex_on_tail(); 
    tail->next_record = r; 
    tail = r; 
    release_mutex_on_tail(); 

    return r; 
} 

(謝罪 - 私はOpenMPフレームワークとマルチスレッド一般には比較的経験がありません。)

+0

OpenMPがpthreads(通常はLinuxまたはosxの場合)上に構築されている場合、動作するはずです。 – tim18

答えて

2

慣用的なミューテックス解決策はOpenMPロックを使用することです:

omp_set_lock(&taillock) 
tail->next_record = r; 
tail = r; 
omp_unset_lock(&taillock) 

とどこか:

omp_lock_t taillock; 
omp_init_lock(&taillock); 

... 

omp_destroy_lock(&taillock); 

簡単なOpenMPのソリューション:

void start_timing(char *name) { 
    Record *r = create_record(name); 
    #pragma omp critical 
    { 
     tail->next_record = r; 
     tail = r; 
    } 
    return r; 
} 

ソースコードの行にバインドされた暗黙のグローバルロックを作成します。詳細な議論については、this questionへの回答を参照してください。

実際には、少なくともOpenMPがPthreadsに基づいているシナリオでは、Pthreadロックを使用することもできます。

警告の単語

パフォーマンス測定コードでのロックの使用は危険です。それでは、しばしばロックの使用を暗示するメモリ割り当てもあります。つまり、start_timeにはかなりのコストがかかり、パフォーマンスはさらに多くのスレッドで悪化します。それは、キャッシュ無効化が、あるスレッドがメモリのチャンク(レコード)を割り当ててから別のスレッドがそれを変更すること(テールポインタ)を持つことを考慮しません。

測定するセクションが数秒かかる場合は問題ありませんが、セクションが数百サイクルに過ぎない場合は大きなオーバーヘッドと摂動が発生します。

スケーラブルなパフォーマンストレース機能を作成するには、スレッドローカルメモリをより大きなチャンクにあらかじめ割り当てておき、各スレッドにそのローカル部分だけを書き込ませる必要があります。

Score-Pなど、既存の測定インフラストラクチャの一部を使用することもできます。

オーバーヘッド&摂動

まず、2つ(リンクされた概念)を区別します。 オーバーヘッドは余分な時間ですが、の摂動は、測定したものへの影響を示します。オーバーヘッドは大量には望ましくないが、摂動ははるかに悪い。

はい、高価な測定ランタイム中にタイマーを一時停止することで、摂動の一部を回避できます(オーバーヘッドが残ります)。しかし、マルチスレッドのコンテキストでは、これは依然として非常に問題です。

  • 1つのスレッドで進捗が遅くなると、他のスレッドがそのスレッドを待っているなどの可能性があります。陰的な障壁の間に。あなたはそのスレッドとそれに続くスレッドの待ち時間をどのように見積もっていますか?
  • 通常、メモリ割り当てはロックされています。測定ランタイム中にメモリを割り当てると、メモリ割り当てに依存する他のスレッドが遅くなります。メモリプールを使って緩和しようとすることもできますが、最初はリンクされたリストを避けます。
+0

スコア-Pリンクをありがとう。私はそれをより完全に調査しなければならないでしょう!質問:私は現在、内部関数に入る前に外部関数のタイミングを「一時停止」しており、内部関数から戻るときに再開するタイミングを述べているはずです。したがって、ロックとmallocは私のタイミング情報に取り込まれるべきではありません。私は、ロックとメモリの割り当て(私はレコードをmalloc、ロックして、それを所定の位置に挿入し、タイミングを開始する)の後までタイミングを開始しないと、まだパフォーマンスのオーバーヘッドの問題がありますか? – tonysdg

+0

私の追加した最後の段落を見てください。 – Zulan

+0

Amazimg答え。どうもありがとうございます! – tonysdg