Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Сравнение и эффективность




В этом месте временно прервем наше последовательное повествование: мы закончили рассмотрение двух наиболее известных, важных и применяемых примитивов синхронизации - семафора и мьютекса. Теперь сделаем короткую остановку и проведем их взаимное сравнение, а также попробуем на примерах оценить затраты процессорной производительности, требуемые этими механизмами.

Дело в том, что на первый взгляд эти два механизма в высшей степени подобны, особенно если речь заходит о бинарном семафоре, принимающем значения счетчика 0 либо 1. Настолько подобны, что и в обсуждениях, и даже в неспециальной литературе можно встретить утверждения, что это «одно и то же». Сейчас мы увидим, что эти два сходных механизма различаются всем: и затратами процессорного времени на их обслуживание, и целями и задачами, которые они призваны решать, и временем жизни... Начнем с простой оценки затрат процессорного времени на обслуживание каждого из механизмов, после чего остальные различия станут нам намного понятнее.

Для проведения таких оценок используем уже применявшуюся нами схему «симметричных» тестов. Почему именно их? Да, здесь нам не требуются в явном виде обменные операции потоков, но воспользуемся «симметричными» тестами просто в силу минимальных переделок того, что уже было написано ранее. Итак, первый вариант теста для мьютекса (файл sy20m.cc):

Скоростные показатели мьютекса

unsigned long N = 1000;

static pthread_barrier_t bstart;

static pthread_mutex_.t mutex - PTHREAD_MUTEX_INITIALIZER;

static bool debug = false;

static char* str;

static volatile int ind = 0;

void* threadfunc (void*data) {
pthread_barrier_wait(&bstart);
unsigned long i = 0;

char tid[ 8 ];

sprintf(tid, "%d", pthread_self()); uint64_t t = 0, t1; while(i++!= N) {

t1 = ClockCyclesO; ptnread_mutex_lock(Smutex); if(debug) str[ ind++ ] = *tid; pthread_mutex_unlock(&mutex); t += ClockCyclesO - t1; sched_yield();

};

cout «pthread_self() «"\t: cycles - "«t «"; on mutex - " «t / N «end!; return NULL;

int main(int argc, char *argv[]) { int opt, val;

while ((opt = getopt(argc, argv, "n;v"))!= -1) { switch(opt) { case 'n":

if{ sscanf(optarg, &val)!= 1)

cout «"parse command line error" «endl, exit(EXIT^FAILURE);

iff val > 0) N = val;

break; case 'v':

debug = true;

break; default:

exit(EXIT,FAILURE);

}

},'

if(debug) str = new char [ 2 * N + 1 ]; const int T = 2; pthread_t tid[ T ];

if(pthreadjDarrier_init(Sbstart, NULL, T)!= EOK) '

perror("barrier init"), exit(EXIT_FAILURE); for(int i = 0; i < T; i++)

if(pthread_create(tid + i, NULL, threadfunc, NULL)!= EOK) perror("thread create"), exit(EXIT_FAILURE); for(int i ~ 0; i < T; i++)

pthread_join(tid[ i ], NULL); if(debug) {

str[ ind J = ДО';

cout «str «endl;

delete [] str;

};

exit(EXIT_SUCCESS);

};

Результаты выполнения этого теста:

# sy20m –n10000

3: cycles - 14644442; on mutex - 146

2: cycles - 14614219; on mutex - 146

# sy20m -n1000000

3: cycles - 146505047; on mutex - 146

2: cycles - 146388673; on mutex - 146

Модифицируем программу, используя вместо мьютекса неименованный бинарный семафор. Для того чтобы не загромождать текст практически тем же кодом, перечислим только необходимые при этом изменения (файл sy20s.cc):

1. Вместо мьютекса объявляем неименованный семафор, а статичес-
кая инициализация мьютекса заменяется на оператор (в теле глав-
ной программы) динамической инициализации семафора с присво-
ением ему начального значения 1:

static sem_t sem;

if(sem_init(&sem, 0, 1)!= 0)

perror("semaphore init"), exit(EXIT_.FAILURE);

2. Функция потока принимает вид:

void* threadfunc (void* data) {

while(i++!= N) { t1 = ClockCyclesO; sem_wait(teem); if(debug) str[ ind++ ] = *tid; sem_post(teem); t += ClockCyclesO - tl; sched_yield();

};

В результате исполнения на этот раз мы получим:

# sy20s –n10000

3: cycles - 87048886; on semaphore - 870

2: cycles - 87077787; on semaphore - 870

# sy20s -nWOOOOO

3: cycles - 869638168; on semaphore - 869
2: cycles - 868725494; on semaphore - 868

Делаем последнюю модификацию в этой группе тестов, теперь используем специфику именованного семафора (файл sy20n.cc):

1. Вместо оператора динамической инициализации неименованного семафора мы теперь должны создать именованный семафор:

static sem_t* sem;

const char semname[] = "/duble";

if((sem = sem_0pen(semname, 0_CREAT, S_IRWX0, 1)) == SEM_FAILED) perrori "semaphore init"), exit{ EXIT_FAILURE);

Примечание

Последний оператор заслуживает отдельного комментария. Техническая документация утверждает, что функция sem_open(), нормально возвращающая указатель созданного дескриптора семафора типа sem_t, в случае ошибки возвращает -1 (так было записано и в самых ранних редакциях P0SIX). Но использование конструкции вида:

if(sem_open(...) = = -1)

просто вызовет синтаксическую ошибку (несоответствие типов) и не пройдет компиляцию.' Естественнее было бы для такой функции возвращать null в случае ошибки, но... так определено в P0SIX. Кроме того, во многих реализациях UNIX определяется константа:

define SEM_FAILED((sem_t*)(-1))

В документации QNX она нигде не упоминается, но, как мы видим, она определена, и все прекрасно работает!

2. Функция потока принимает вид (теперь sem, в отличие от предшествующего случая, ведь теперь это уже указатель на переменную типа sem_t):

void* threadfunc (void* data) {

while(i++!= N) { t1 a ClockCyclesO; sem_wait(sem);

if(debug) str[ t id++ ] = *tid;

sem_post(sem);

t += ClockCyclesO - t1;

sched_yield();

};

};

3. Теперь особое внимание необходимо уделить не только созданию,
но и ликвидации именованного семафора (такой семафор имеет вре-
мя жизни ядра системы и будет продолжать свое существование
и после завершения нашего приложения):

sem_close(sem); sem_unlink(semname);

Запустим полученное приложение при таком значении -п, которое обеспечит достаточное время его работы. Прежде чем обсуждать полученные результаты, посмотрим отображение семафора на пространство файловых имен системы во время работы приложения:

# Is -1 /dev/sem total 1

n г-х 1 root root 1 Feb 10 18:56 duble

А теперь и результаты работы программы:

#nice -n-19 sy20n –n100000

3: cycles - 1453746002; on semaphore - 14537

2; cycles - 14.54203573; on semaphore - 14542

Наконец, мы можем обратиться к количественному анализу полученных цифр:

Примитивы - мьютекс, неименованный и именованный семафоры, -кажущиеся на первый взгляд сходными, требуют для своего обслуживания в эквивалентных условиях принципиально различных затрат, величины которых радикально отличаются: 140 - 870 - 14500 процессорных циклов соответственно, что соотносится как 1:6,2:104.

Так же радикально отличаются и их характеристики доступа: изнутри процесса (или даже только из того потока, который уже владеет мьютексом), из внешнего процесса, из процесса, работающего на совершенно другом сетевом узле... То, что мы уже рассматривали как «характеристики времени жизни» объектов, принадлежит различным категориям: процесса (process-persistent), ядра (kernel-persistent) или файловой системы (filesystem-persistent).

У захваченного мьютекса всегда есть поток-владелец, и только он может освободить его в дальнейшем. Именно поэтому мьютекс может использоваться для синхронизации потоков, но только синхронизации в смысле разграничения временной последовательности доступа к фрагменту кода - к тому, что часто называют критической секцией кода. Функциональность семафора значительно выше: при возможности (почти всегда) его применения в том контексте, в котором используется и мьютекс (только нужно ли это делать?), он может применяться и для синхронизации потоков в смысле координации последовательности их взаимодействия в качестве элемента, управляющего порядком выполнения. Покажем это на примере. Для этого незначительно трансформируем код предыдущего теста для семафора (файл sy21.cc):




Поделиться с друзьями:


Дата добавления: 2014-12-10; Просмотров: 370; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.021 сек.