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

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

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

虽然这里的其他答案一般都很有用,但问题的具体答案是你不应该使用clock()来比较时间。 clock()测量跨线程累加的CPU时间。 在核心之间拆分作业时,它至少使用相同的CPU时间(通常由于线程开销而更多)。 在此页面上搜索clock(),以查找“如果进程是multithreading的,则会添加所有单个进程线程占用的CPU时间”。

只是作业在线程之间分配,所以你需要等待的总时间就少了。 你应该使用壁挂时间(挂钟上的时间)。 OpenMP提供了一个例程omp_get_wtime()来完成它。 以下面的例程为例:

 #include  #include  #include  #include  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参考声明,GCD使用gettimeofday作为gettimeofday时间。 所以,我创建了一个新的Cocoa App,并且在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以及为什么iterations在这种情况下与num_threads大致相当。

编辑:我注意到雅各布的回答包括

我在并行化循环中使用omp_get_thread_num()函数来打印出它正在处理的核心......这样你可以确定它在两个核心上运行。

这是不正确的(它已被编辑部分修复)。 使用omp_get_thread_num()确实是确保代码是multithreading的好方法,但它没有显示“它正在处理哪个核心”,只是哪个线程。 例如,以下代码:

 #include  #include  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上 - 转到Window/CPU Usage ),您可以看到在核心之间切换的作业,因此核心!=线程。

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

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

没有任何细节,很难猜到。 也许您的应用程序甚至不受CPU限制。 您的代码运行时是否观察CPU负载? 它至少在一个核心上达到了100%吗?

您的问题缺少一些非常关键的细节,例如您的应用程序的性质,您尝试改进的部分,分析结果(如果有的话)等等……

说过在接近性能改进工作时你应该记住几个关键点:

  • 应该始终集中精力于通过分析certificate效率低下的代码区域
  • 并行化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 #pragma omp parallel for default(none)shared(a,b,c)

这样您可以确保它在两个核心上运行,因为只会创建2个线程。

顺便说一下,在编译时是否启用了OpenMP? 在Visual Studio中,您必须在Property PagesC++ -> Language启用它,并将OpenMP Support设置为Yes