QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович
Шрифт:
Интервал:
Закладка:
Если поток много раз вытесняется в период своей работы с основным приоритетом, то его выполнение может превратиться в многократное колебание с высокой частотой между основным и фоновым приоритетами. Поэтому в QNX 6.2.1 в параметрах для спорадической диспетчеризации можно установить (ограничить) максимальное количество пополнений бюджета за период.
Как уже описывалось выше, структура shed_paramсодержит в своем составе, в частности, еще и структуру параметров для спорадической диспетчеризации (при других типах диспетчеризации эта часть не используется):
struct {
_INT32 __ss_low_priority;
_INT32 __ss_max_repl;
struct timespec __ss_repl_period;
struct timespec __ss_init_budget;
} __ss;
где low_priority— фоновый приоритет; max_repl— максимальное количество пополнений бюджета за период; repl_period— период пополнения бюджета и init_budget — начальный бюджет.
Соображения производительности
Выполним «симметричный» тест аналогично тому, как это делалось для переключения контекстов процессов (стр. 44), но теперь применительно к потокам ( файл p5t.cc). При этом мы постараемся максимально сохранить принципы функционирования, имевшие место в приложении «Затраты на взаимное переключение процессов» ( файл p5.сс) (естественно, из-за принципиального различия механизмов тексты кодов будут существенно отличаться).
Затраты на взаимное переключение потоков#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <iostream.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/neutrino.h>
unsigned long N = 1000;
// потоковая функция:
void* threadfunc(void* data) {
uint64_t t = ClockCycles();
for (unsigned long i = 0; i < N; i++) sched_yield();
t = ClockCycles() - t;
// дать спокойно завершиться 2-му потоку до начала вывода
delay(100);
cout << pthread_self() << "t: cycles - " << t
<< ", on sched - " << (t / N) / 2 << endl;
return NULL;
}
int main(int argc, char* argv[]) {
int opt, val;
while ((opt = getopt(argc, argv, "n:")) != -1) {
switch(opt) {
case 'n': // переопределения числа переключений
if (sscanf(optarg, "%i", &val) != 1)
cout << "parse command line error" << endl, exit(EXIT_FAILURE);
if (val > 0) N = val;
break;
default:
exit(EXIT_FAILURE);
}
}
const int T = 2;
pthread_t tid[T];
// создать взаимодействующие потоки
for (int i = 0; i < T; i++)
if (pthread_create(tid + i, NULL, threadfunc, NULL) != EOK)
cout << "thread create error", exit(EXIT_FAILURE);
// и дожидаться их завершения ...
for (int i = 0; i < T; i++)
pthread_join(tid[i], NULL);
exit(EXIT_SUCCESS);
}
Результаты выполнения программы:
# nice -n-19 p5t -n100
2 : cycles - 79490; on sched - 397
3 : cycles - 78350; on sched — 391
# nice -n-19 p5t -n1000
2 : cycles - 753269; on sched - 376
3 : cycles - 752069; on sched - 376
# nice -n-19 p5t -n10000
2 : cycles - 7494255; on sched - 374
3 : cycles - 7493225; on sched - 374
# nice -n-19 p5t -n100000
2 : cycles - 74897795; on sched - 374
3 : cycles - 74895800; on sched — 374
# nice -n-19 p5t -n1000000
2 : cycles - 748850811, on sched - 374
3 : cycles - 748850432; on sched - 374
Как и в случае с процессами, результаты отличаются очень высокой устойчивостью при изменении «объема вычислений» на 4 порядка, однако по своим величинам значения для потоков почти в 2 раза меньше, чем для процессов (стр. 45).
Завершение потока
Как и в случае обсуждавшегося ранее завершения процесса, для потоков мы будем отчетливо различать случаи:
• «естественного» завершения выполнения потока из кода самого потока;
• завершения потока извне, из кода другого потока или по сигналу. Для этого действия, в отличие от «естественного» завершения, будем использовать другой термин — отмена.
Завершение потока происходит при достижении функцией потока своего естественного конца и выполнения оператора return(явно или неявно) или выполнения потоком вызова: