所以我意识到这个问题听起来很愚蠢(是的,我使用的是双核),但是我尝试了两个不同的库(Grand Central Dispatch和OpenMP),并且使用clock()来计算代码的时间和不使用使它平行的线,速度是一样的。 (为了记录,他们都使用自己的平行形式)。他们报告说在不同的线程上运行,但也许它们运行在同一个核心上?有没有办法检查? (这两个库都用于C,我在较低层时感到不舒服。)这非常奇怪。有什么想法吗?
答案 0 :(得分:19)
编辑:为响应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 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
在这种情况下dispatch_apply
与iterations
大致相当的原因,请参阅联接页面上的num_threads
{/ 1}}。
我使用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上 - 转Window/CPU Usage
),你可以看到在核心之间切换的作业,所以核心!=线程。
答案 1 :(得分:8)
很可能您的执行时间不受并行化的循环的限制。
我的建议是,您可以查看代码以查看大部分时间内的内容。大多数工程师都会告诉你,在做任何激烈的事情来优化事情之前,你应该这样做。
答案 2 :(得分:2)
没有任何细节,很难猜到。也许您的应用程序甚至不受CPU限制。您的代码运行时是否观察CPU负载?它至少在一个核心上达到了100%吗?
答案 3 :(得分:1)
您的问题缺少一些非常重要的细节,例如您的应用程序的性质,您尝试改进的部分,分析结果(如果有的话)等......
说过在接近绩效改进工作时你应该记住几个关键点:
确保你不会反对这些观点,因为一个有根据的猜测(除非有任何其他细节)会说这正是你正在做的事情。
答案 4 :(得分:0)
如果你在循环中使用大量内存,可能会阻止它更快。您也可以查看pthread库,手动处理线程。
答案 5 :(得分:0)
如果您未指定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中,您必须在属性页,C++ -> Language
中启用它并将OpenMP Support
设置为Yes