当我使用并行代码时,为什么我的计算机没有显示加速?

Jar*_*tar 6 c parallel-processing performance openmp grand-central-dispatch

所以我意识到这个问题听起来很愚蠢(是的,我使用的是双核),但是我尝试了两个不同的库(Grand Central Dispatch和OpenMP),并且在使用clock()来计算带有和不带有线条的代码时它平行,速度是一样的.(为了记录,他们都使用自己的平行形式).他们报告说在不同的线程上运行,但也许它们运行在同一个核心上?有没有办法检查?(这两个库都用于C,我在较低层时感到不舒服.)这非常奇怪.有任何想法吗?

Ram*_*nka 19

编辑:为响应OP评论添加了Grand Central Dispatch的详细信息.

虽然这里的其他答案一般都很有用,但您问题的具体答案是您不应该clock()用来比较时间.clock()测量在线程中累加的CPU时间.在核心之间拆分作业时,它至少使用尽可能多的CPU时间(通常由于线程开销而更多).搜索时钟()这个页面,找到"如果过程是多线程的,通过过程的所有各个线程消耗的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

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
Run Code Online (Sandbox Code Playgroud)

你可以看到clock()时间没有太大变化.没有得到0.254 pragma,所以使用openMP和一个线程比没有使用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));
}
Run Code Online (Sandbox Code Playgroud)

我在控制台上得到以下结果:

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
Run Code Online (Sandbox Code Playgroud)

这与我上面的情况大致相同.

这是一个非常人为的例子.实际上,您需要确保将优化保持在-O0,否则编译器将意识到我们不进行任何计算而根本不进行循环.此外,我所采用的整数cos在两个示例中是不同的,但这不会对结果产生太大影响.请参阅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());
}
Run Code Online (Sandbox Code Playgroud)

打印出它使用线程0到49,但这并没有显示它正在处理哪个核心,因为我只有8个核心.通过查看活动监视器(OP提到GCD,所以必须在Mac上去Window/CPU Usage),你可以看到在核心之间切换的作业,所以核心!=线程.


T.E*_*.D. 8

很可能您的执行时间不受并行化的循环的约束.

我的建议是,您可以对代码进行分析,以查看大部分时间内的内容.大多数工程师会告诉你,做任何激烈的事情之前你应该这样做以优化事物.