编辑:为响应 OP 评论添加了 Grand Central Dispatch 的详细信息。
虽然此处的其他答案通常很有用,但您的问题的具体答案是您不应该使用 clock() 来比较时间。 clock() 测量跨线程累加的 CPU 时间。当您在内核之间拆分作业时,它至少会使用尽可能多的 CPU 时间(由于线程开销,通常会多一点)。在this页面上搜索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() 时间没有太大变化。如果没有pragma,我得到 0.254,因此使用带有一个线程的 openMP 比完全不使用 openMP 慢一点,但是每个线程的挂墙时间都会减少。
由于某些计算部分不并行(请参阅Amdahl's_law)或不同线程争夺相同内存等原因,改进并不总是那么好。
编辑:对于 Grand Central Dispatch,GCD reference 声明 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 的整数不同,但这不会对结果产生太大影响。请参阅手册页上的STRIDE 了解dispatch_apply 的正确操作方法以及为什么iterations 在这种情况下与num_threads 大致相当。
编辑:我注意到雅各布的回答包括
我使用 omp_get_thread_num()
在我的并行循环中运行
打印出它正在工作的核心
on... 这样你就可以确定
它在两个内核上运行。
这是不正确的(已通过编辑部分修复)。使用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,但这并没有显示它正在处理哪个内核,因为我只有八个内核。通过查看活动监视器(OP 提到了 GCD,所以必须在 Mac 上 - 转到 Window/CPU Usage),您可以看到作业在内核之间切换,因此 core != thread。