したがって、この質問はばかげているように聞こえますが(はい、デュアルコアを使用しています)、2つの異なるライブラリ(Grand Central DispatchとOpenMP)を試しました。また、clock()を使用して、並行して、速度は同じです。(記録のために、彼らは両方とも独自の形式の並列を使用していました)。彼らは異なるスレッドで実行されていると報告していますが、おそらく同じコアで実行されていますか?確認する方法はありますか?(どちらのライブラリもC用ですが、下位層では不快です。)これは非常に奇妙です。何か案は?
6 に答える
編集:OPコメントに応じてGrandCentralDispatchの詳細を追加しました。
ここでの他の回答は一般的に役立ちますが、あなたの質問に対する具体的な回答はclock()
、タイミングを比較するために使用すべきではないということです。clock()
スレッド間で合計されるCPU時間を測定します。ジョブをコア間で分割する場合、少なくとも同じくらいのCPU時間を使用します(通常、スレッドのオーバーヘッドのために少し長くなります)。このページでclock()を検索して、「プロセスがマルチスレッドの場合、プロセスのすべての個々のスレッドによって消費されるCPU時間が追加されます」を見つけます。
ジョブがスレッド間で分割されるだけなので、待機する必要のある全体的な時間は短くなります。壁時計(壁時計の時間)を使用する必要があります。OpenMPはomp_get_wtime()
それを行うためのルーチンを提供します。例として、次のルーチンを取り上げます。
#include <omp.h>
#include <time.h>
#include <math.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int i, nthreads;
clock_t clock_timer;
double wall_timer;
for (nthreads = 1; nthreads <=8; nthreads++) {
clock_timer = clock();
wall_timer = omp_get_wtime();
#pragma omp parallel for private(i) num_threads(nthreads)
for (i = 0; i < 100000000; i++) cos(i);
printf("%d threads: time on clock() = %.3f, on wall = %.3f\n", \
nthreads, \
(double) (clock() - clock_timer) / CLOCKS_PER_SEC, \
omp_get_wtime() - wall_timer);
}
}
結果は次のとおりです。
1 threads: time on clock() = 0.258, on wall = 0.258
2 threads: time on clock() = 0.256, on wall = 0.129
3 threads: time on clock() = 0.255, on wall = 0.086
4 threads: time on clock() = 0.257, on wall = 0.065
5 threads: time on clock() = 0.255, on wall = 0.051
6 threads: time on clock() = 0.257, on wall = 0.044
7 threads: time on clock() = 0.255, on wall = 0.037
8 threads: time on clock() = 0.256, on wall = 0.033
clock()
時間はあまり変わらないことがわかります。を使用しない場合は0.254になるpragma
ため、openMPをまったく使用しない場合よりも1つのスレッドでopenMPを使用する場合の方が少し遅くなりますが、スレッドごとに壁時間が短くなります。
たとえば、計算の一部が並列ではない( Amdahl's_lawを参照)か、同じメモリ上で異なるスレッドが競合しているため、改善が必ずしもこれほど良いとは限りません。
編集:Grand Central Dispatchの場合、GCDリファレンスには、GCDが実gettimeofday
時間に使用すると記載されています。そこで、新しいCocoaアプリを作成し、次のように入力しますapplicationDidFinishLaunching
。
struct timeval t1,t2;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int iterations = 1; iterations <= 8; iterations++) {
int stride = 1e8/iterations;
gettimeofday(&t1,0);
dispatch_apply(iterations, queue, ^(size_t i) {
for (int j = 0; j < stride; j++) cos(j);
});
gettimeofday(&t2,0);
NSLog(@"%d iterations: on wall = %.3f\n",iterations, \
t2.tv_sec+t2.tv_usec/1e6-(t1.tv_sec+t1.tv_usec/1e6));
}
コンソールに次の結果が表示されます。
2010-03-10 17:33:43.022 GCDClock[39741:a0f] 1 iterations: on wall = 0.254
2010-03-10 17:33:43.151 GCDClock[39741:a0f] 2 iterations: on wall = 0.127
2010-03-10 17:33:43.236 GCDClock[39741:a0f] 3 iterations: on wall = 0.085
2010-03-10 17:33:43.301 GCDClock[39741:a0f] 4 iterations: on wall = 0.064
2010-03-10 17:33:43.352 GCDClock[39741:a0f] 5 iterations: on wall = 0.051
2010-03-10 17:33:43.395 GCDClock[39741:a0f] 6 iterations: on wall = 0.043
2010-03-10 17:33:43.433 GCDClock[39741:a0f] 7 iterations: on wall = 0.038
2010-03-10 17:33:43.468 GCDClock[39741:a0f] 8 iterations: on wall = 0.034
これは私が上で得ていたのとほぼ同じです。
これは非常に工夫された例です。実際、最適化を-O0に維持する必要があります。そうしないと、コンパイラーは、計算を保持せず、ループをまったく実行しないことを認識します。また、私が取っている整数cos
は2つの例で異なりますが、結果にはあまり影響しません。それを適切に行う方法と、この場合に広く匹敵する理由についてはSTRIDE
、マンページのを参照してください。dispatch_apply
iterations
num_threads
編集:ジェイコブの答えには次のものが含まれていることに注意してください
並列化されたループ内でomp_get_thread_num()関数を使用して、動作しているコアを出力します...このようにして、両方のコアで実行されていることを確認できます。
これは正しくありません(編集によって部分的に修正されています)。使用omp_get_thread_num()
することは、コードがマルチスレッドであることを確認するための良い方法ですが、「どのコアで動作しているか」ではなく、どのスレッドであるかを示します。たとえば、次のコードです。
#include <omp.h>
#include <stdio.h>
int main() {
int i;
#pragma omp parallel for private(i) num_threads(50)
for (i = 0; i < 50; i++) printf("%d\n", omp_get_thread_num());
}
スレッド0から49を使用していることを出力しますが、コアが8つしかないため、作業中のコアは表示されません。アクティビティモニター(OPはGCDに言及しているので、Mac上にある必要があります-go Window/CPU Usage
)を見ると、コア間でジョブが切り替わっていることがわかります。つまり、コア!=スレッドです。
ほとんどの場合、実行時間は、並列化したループに拘束されません。
私の提案は、コードのプロファイルを作成して、ほとんどの時間に何がかかっているかを確認することです。ほとんどのエンジニアは、物事を最適化するために抜本的なことをする前に、これを行うべきだと言うでしょう。
詳細なしで推測するのは難しいです。たぶん、あなたのアプリケーションはCPUに縛られていません。コードの実行中にCPUの負荷を監視しましたか?少なくとも1つのコアで100%ヒットしましたか?
あなたの質問には、アプリケーションの性質、改善しようとしている部分、結果のプロファイリング(ある場合)など、いくつかの非常に重要な詳細が欠けています...
パフォーマンス改善の取り組みに取り組む際には、いくつかの重要なポイントを覚えておく必要があると述べました。
- プロファイリングによって非効率的であることが証明されているコード領域に常に集中する必要があります。
- CPUバウンドコードを並列化しても、パフォーマンスが向上することはほとんどありません(シングルコアマシンの場合)。不要なコンテキストスイッチで貴重な時間を失い、何も得られなくなります。これを行うことで、パフォーマンスを非常に簡単に悪化させることができます。
- マルチコアマシンでCPUバウンドコードを並列化する場合でも、並列実行の保証はないことを覚えておく必要があります。
知識に基づいた推測(追加の詳細を除いて)はまさにそれがあなたがしていることであると言うので、あなたがこれらの点に反対していないことを確認してください。
ループ内で大量のメモリを使用している場合は、ループの高速化が妨げられる可能性があります。また、手動でスレッドを処理するために、pthreadライブラリを調べることもできます。
omp_get_thread_num()
並列化されたループ内の関数を使用して、指定しない場合にnum_threads
動作しているコアを出力します。たとえば、
printf("Computing bla %d on core %d/%d ...\n",i+1,omp_get_thread_num()+1,omp_get_max_threads());
上記は、このプラグマに対して機能します#pragma omp parallel for default(none)shared(a、b、c)
このようにして、2つのスレッドのみが作成されるため、両方のコアで実行されていることを確認できます。
ところで、コンパイル時にOpenMPは有効になっていますか?Visual Studioでは、プロパティページで有効にし、次のようにC++ -> Language
設定OpenMP Support
する必要があります。Yes