QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович Страница 36
QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович читать онлайн бесплатно
• При статическомвыполнении (фиксированном количестве параллельных ветвей в приложении) эффективность приложений, построенных на параллельных потоках или параллельных процессах, практически не отличается. Более того, эффективности таких приложений не отличаются и от классической последовательной организации приложения, работающего в одном потоке.
• Существует дополнительный фактор, обеспечивающий «легковесность» потоков в противовес процессам, — это легкость и эффективность их взаимодействия в едином адресном пространстве. В случае процессов для обеспечения таких взаимодействий возникает необходимость привлечения «тяжеловесных» механизмов IPC разнообразной природы (именованные и неименованные каналы, разделяемая память, обмен UNIX-сообщениями и другие). При рассмотрении обмена сообщениями QNX мы еще раз убедимся в том, что обмены и взаимодействия между процессами могут требовать весьма существенных процессорных ресурсов, а при обменах с интенсивным трафиком могут стать доминирующей компонентой, определяющей пределы реальной производительности системы.
Пример: синхронное выполнение кода
Выше приводилось достаточно много подобных примеров, но это были примеры, так сказать, «локальные», фрагментарные, иллюстрирующие использование какой-то одной возможности применительно к потокам. Сейчас мы приведем пример, реализующий часто возникающую на практике возможность. Некоторые программные действия (функции) мы хотели бы запускать периодически с фиксированным временным интервалом T, что весьма напоминает действия и аппаратной реализации, которые должны быть выполнены по каждому импульсу «синхронизирующей последовательности».
Простейшая реализация могла бы выглядеть так:
...
while(true) {
delay(T);
func();
}
Но это очень «слабое» решение:
• Задержка, обеспечиваемая функцией пассивной задержки delay(), согласно требованиям POSIX не может быть меньшеуказанного параметра T, но... может быть сколь угодно больше! (В [4] мы писали, что при T = 1 реальная величина задержки будет составлять не 1 мсек., как можно было бы ожидать, а с большой степенью вероятности 3 мсек., и там же мы подробно показывали, как это происходит.)
• Если в системе одновременно с этим приложением работает процесс (поток) более высокого приоритета, то наше приложение может вообще никогда «не проснуться», по крайней мере, пока это не «соизволит» санкционировать параллельное приложение.
• Здесь мы обеспечиваем только одну синхронизированную последовательность вызовов функции func(). А если бы нам потребовалось несколько (много) синхросерий, в каждой из которых выполняется своя функция, а периоды серий не кратны друг другу?
• Наконец, время выполнения целевой функции func()включается в период одного «кругового пробега» цикла, то есть период T отсчитывается от концапредыдущего выполнения функции до началатекущего, а это не совсем то, что мы подразумевали при использовании термина «синхронное».
• Более того, если время выполнения функции func()достаточно флуктуирует от одного вызова до другого (например, из-за изменений данных, с которыми работает функция), то периоды вызовов начинают «гулять», а дисперсия периода результирующей последовательности вызовов func()становится просто непомерно большой.
Ниже показано решение, свободное от многих из этих недостатков ( файл t3.cc). Приложение представляет собой тестовую программу, осуществляющую 3 цепочки выполнения различных целевых функций ( mon1, mon2, mon3) с разными периодами для каждой цепочки (массив period[]):
Синхронизация выполнения участка кода#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <inttypes.h>
#include <errno.h>
#include <iostream.h>
#include <sys/neutrino.h>
#include <sys/syspage.h>
#include <sys/netmgr.h>
#include <pthread.h>
#include <signal.h>
#include <algorithm>
static void out(char s) {
int policy;
sched_param param;
pthread_getschedparam(pthread_self(), &policy, &param);
cout << s << param.sched_curpriority << flush;
}
// целевые функции каждой из последовательностей только
// выводят свой символ-идентификатор и следующий за ним
// приоритет, на котором выполняется целевая функция
static void mon1(void) { out('.'); }
static void mon2(void) { out('*'); }
static void mon3(void) { out('+'); }
// это всего лишь перерасчет временных интервалов,
// измеренных в тактах процессора (в наносекундах)
inline uint64_t cycles2nsec(uint64_t с) {
const static uint64_t cps =
// частота процессора
SYSPAGE_ENTRY(qtime)->cycles_per_sec;
return (с * 1000000000) / cps;
}
// структура, необходимая только для накопления статистики параметров
// ряда временных отметок: среднего, среднеквадратичного отклонения,
// минимального и максимального значений
struct timestat {
private:
uint64_t prev;
public:
uint64_t num;
double mean, disp, tmin, tmax;
timestat(void) {
Жалоба
Напишите нам, и мы в срочном порядке примем меры.