workproc((int)data);
trtime[id].f = ClockCycles();
pthread_barrier_wait(&bfinish);
return NULL;
}
int main(int argc, char *argv[]) {
// здесь только обработка многочисленных ключей...
int opt, val, nthr = 1, nall = SHRT_MAX;
while ((opt = getopt(argc, argv, 't:n:p:a:')) != -1) {
switch(opt) {
case 't':
if (sscanf(optarg, '%i', &val) != 1)
perror('parse command line failed'), exit(EXIT_FAILURE);
if (val > 0 && val <= SHRT_MAX) nthr = val;
break;
case 'p':
if (sscanf(optarg, '%i', &val) != 1)
perror('parse command line failed'), exit(EXIT_FAILURE);
if (val != getprio(0))
if (setprio(0, val) == -1)
perror('priority isn't a valid'), exit(EXIT_FAILURE);
break;
case 'n':
if (sscanf(optarg, '%i', &val) != 1)
perror('parse command line failed'), exit(EXIT_FAILURE);
if (val > 0)
nsingl *= val;
break;
case 'a':
if (sscanf(optarg, '%i', &val) != 1)
perror('parse command line failed'), exit(EXIT_FAILURE);
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 = '
<< clcout.nsec * 4 / 1000000. << endl;
// калибровка времени выполнения в одном потоке
const int NCALIBR = 512;
uint64_t tmin = 0, tmax = 0;
tmin = ClockCycles();
workproc(NCALIBR);
tmax = ClockCycles();
cout << 'calculating = '
<< 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 = '
<< 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