QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович
Шрифт:
Интервал:
Закладка:
if (val > 0) nall = val;
break;
default:
exit(EXIT_FAILURE);
}
}
// ... вот здесь начинается собственно сама программа.
if (nthr > 1)
cout << "Multi-thread evaluation, thread number = " << nthr;
else cout << "Single-thread evaluation";
cout << " , priority level: " << getprio(0) << endl;
__clockperiod clcout;
ClockPeriod(CLOCK_REALTIME, NULL, &clcout, 0);
// интервал диспетчеризации - 4 периода tickslice
// (системного тика):
cout << "rescheduling = t"
<< clcout.nsec * 4 / 1000000. << endl;
// калибровка времени выполнения в одном потоке
const int NCALIBR = 512;
uint64_t tmin = 0, tmax = 0;
tmin = ClockCycles();
workproc(NCALIBR);
tmax = ClockCycles();
cout << "calculating = t"
<< cycle2milisec(tmax - tmin) / NCALIBR << endl;
// а теперь контроль времени многих потоков
if (pthread_barrier_init(&bstart, NULL, nthr) != EOK)
perror("barrier init"), exit(EXIT_FAILURE);
if (pthread_barrier_init(&bfinish, NULL, nthr + 1) != EOK)
perror("barrier init"), exit(EXIT_FAILURE);
trtime = new interv[nthr];
int cur = 0, prev = 0;
for (int i = 0; i < nthr; i++) {
// границы участков работы для каждого потока.
cur = (int)floor((double)nall / (double)nthr * (i + 1) + .5);
prev = (int)floor((double)nall / (double)nthr * i + 5);
if (pthread_create(NULL, NULL, threadfunc, (void*)(cur - prev)) != EOK)
perror("thread create"), exit(EXIT_FAILURE);
}
pthread_barrier_wait(&bfinish);
for (int i=0; i < nthr; i++ ) {
tmin = (i == 0) ? trtime[0].s : __min(tmin, trtime[i].s);
tmax = ( i == 0 ) ? trtime[0].f : __max(tmax, trtime[i].f);
}
cout << "evaluation = t"
<< cycle2milisec(tmax - tmin) / nall << endl;
pthread_barrier_destroy(&bstart);
pthread_barrier_destroy(&bfinish);
delete trtime;
exit(EXIT_SUCCESS);
}
Логика этого приложения крайне проста:
• Есть некоторая продолжительная по времени рабочая функция ( workproc), выполняющая массированные вычисления.
• Многократно (это число определяется ключом запуска а )выполняется рабочая функция. Хорошо (то есть корректнее), если время ее единичного выполнения, которое задается ключом n, больше интервала диспетчеризации системы (в системе установлена диспетчеризация по умолчанию - круговая, или карусельная).
• Весь объем этой работы делится поровну (или почти поровну) между несколькими (ключ t) потоками.
• Сравниваем усредненное время единичного выполнения рабочей функции для разного числа выполняющих потоков (в выводе "calculating"— это время эталонного вычисления в одном главном потоке, a "evaluation"— время того же вычисления, но во многих потоках).
• Для того чтобы иметь еще большую гибкость, предоставляется возможность переопределять приоритет, под которым в системе все это происходит (ключ p).
Вот самая краткая сводка результатов (1-я строка вывода переносится для удобства чтения):
# t1 -n1 -t1000 -a2000
Multi-thread evaluation, thread number = 1000, priority level: 10
rescheduling = 3.99939
calculating = 1.04144
evaluation = 1.08001
# t1 -n1 -t10000 -a20000
Multi-thread evaluation, thread number = 10000, priority level: 10
rescheduling = 3.99939
calculating = 1.04378
evaluation = 1.61946
# t1 -n5 -a2000 -t1
Single-thread evaluation, priority level: 10
rescheduling = 3.99939
calculating = 5.07326
evaluation = 5.04726
# t1 -n5 -a2000 -t2
Multi-thread evaluation, thread number = 2, priority level: 10
rescheduling = 3.99939
calculating = 5.06309
evaluation = 5.04649
# t1 -n5 -a2000 -t20
Multi-thread evaluation, thread number = 20, priority level: 10