`
Читать книги » Книги » Компьютеры и Интернет » Интернет » QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович

QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович

1 ... 67 68 69 70 71 ... 106 ВПЕРЕД
Перейти на страницу:

memcpy((void*)atomic_add_value(ind, how), (void*)res, how);

В последнем случае это, конечно, трюкачество, построенное на том, что в 32-разрядной архитектуре представления указателя и переменной unsigned intсовпадают, но это «работающее трюкачество» и работающее иногда весьма эффективно.

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

cout << "Это вывод потока " << pthread_self() << endl;

Но так делать нельзя: при таком решении у нас появляются 2 проблемы, одна из которых очевидна, а другая — не очень:

1. Выводы разных потоков могут «смешиваться»; более того, за счет буферизации вывода некоторые «рваные» фрагменты мы будем наблюдать дважды. Одним словом, наш вывод окажется полностью «нечитабельным».

2. При осуществлении вывода в контексте потока почти наверняка в процессе вывода будут выполняться системные вызовы, которые потребуют новой диспетчеризации и приведут к вытеснению исходного потока. При этом порядок передачи управления от потока к потоку при наличии отладочного вывода будет отличаться от порядка при его отсутствии. А это, наверное, не совсем то, что мы ожидали. В результате при наличии отладочного вывода мы можем наблюдать совсем не ту картину, для изучения которой этот вывод, собственно, и предназначен.

Эти эффекты не связаны с какой-то конкретной формой вывода, такой как вывод в поток, показанный выше; точно так же будет вести себя и традиционный вызов printf(...).

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

const int N = ... , M = ...;

char buf[N];

volatile unsigned ind = 0;

...

// а вот это производится из каждого потока

char tbuf[M];

sprintf(tbuf, "Это вывод потока %X", pthread_self());

strcpy(buf + atomic_add_value(ind, strlen(tbuf)), tbuf);

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

Сравнение мьютекса и двух форм вызова атомарной операции

#include <stdlib.h>

#include <stdio.h>

#include <iostream.h>

#include <unistd.h>

#include <inttypes.h>

#include <pthread.h>

#include <sys/neutrino.h>

#include <atomic.h>

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

 uint64_t N = 100000;

 bool atom = false, value = false;

 int opt, val;

 while ((opt = getopt(argc, argv, "n:av")) != -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;

  // использовать атомарные операции

  case 'a':

   atom = true;

   break;

  // использовать форму, возвращающую значение

  case 'v':

   value = true;

   break;

  default:

   exit(EXIT_FAILURE);

  }

 }

 // замеряется количество процессорных циклов для каждого случая

 uint64_t i = N, t = ClockCycles();

 volatile unsigned ind = 0;

 if (!atom) {

  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  while (i--) {

   pthread_mutex_lock(&mutex);

   ind++;

   pthread_mutex_unlock(&mutex);

  }

 } else if (value)

  while (i--) atomic_add_value(&ind, 1);

 else while (i--) atomic_add(&ind, 1);

 t = ClockCycles() - t;

 cout << "all cycles - " << t << "; on operation - "

  << t / N << endl;

 exit(EXIT_SUCCESS);

}

Вот результат при использовании критической секции:

# nice -n-19 a1 -n10000000

all cycles - 1120872156; on operation - 112

Результат с применением атомарной операции, не возвращающей значения:

# nice -n-19 a1 -n10000000 -a

all cycles — 391018203; on operation - 39

Результат с применением атомарной операции, возвращающей значение (обещанная разница составляет порядка 10%):

1 ... 67 68 69 70 71 ... 106 ВПЕРЕД
Перейти на страницу:

Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович, относящееся к жанру Интернет. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.

Комментарии (0)