РЕДАКТИРОВАТЬ: Добавлены детали для Grand Central Dispatch в ответ на комментарий OP.
Хотя остальные ответы здесь в целом полезны, конкретный ответ на ваш вопрос заключается в том, что вам не следует использовать clock()
для сравнения времени. clock()
измеряет время процессора, которое складывается между потоками. Когда вы разделяете работу между ядрами, она использует как минимум столько же процессорного времени (как правило, немного больше из-за непроизводительных издержек). Выполните поиск clock () на этой странице, чтобы найти «Если процесс многопоточный, добавляется время процессора, потребляемое всеми отдельными потоками процесса».
Просто работа распределяется между потоками, поэтому общее время ожидания меньше. Вы должны использовать настенное время (время на настенных часах). 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 с одним потоком немного медленнее, чем вообще без использования openMP, но время задержки уменьшается с каждым потоком.
Улучшение не всегда будет таким хорошим из-за, например, частей вашего вычисления, которые не параллельны (см. Amdahl's_law ) или различных потоков, сражающихся за одну и ту же память.
EDIT: Для Grand Central Dispatch ссылка GCD гласит, что GCD использует gettimeofday
для настенного времени. Итак, я создаю новое приложение Какао, и в 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 ()
функция внутри моего параллельного цикла
распечатать какое ядро работает
на ... Таким образом, вы можете быть уверены, что
он работает на обоих ядрах.
, что не правильно (это было частично исправлено редактированием). Использование 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, но это не показывает, на каком ядре он работает, так как у меня только восемь ядер. Посмотрев на Activity Monitor (OP упоминал GCD, поэтому должен быть на Mac - go Window/CPU Usage
), вы можете увидеть, как задания переключаются между ядрами, поэтому core! = Thread.