Жанры книг
Мы в соц.сетях
ParaKnig📚 » Компьютеры и Интернет » Интернет » QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович
<< На главную

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 65 66 67 68 69 70 71 72 73 ... 106
Перейти на страницу:

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

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

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

cout &lt;&lt; &quot;Это вывод потока &quot; &lt;&lt; pthread_self() &lt;&lt; endl;

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

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

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

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

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

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

char buf[N];

volatile unsigned ind = 0;

...

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

char tbuf[M];

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

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

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

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

#include &lt;stdlib.h&gt;

#include &lt;stdio.h&gt;

#include &lt;iostream.h&gt;

#include &lt;unistd.h&gt;

#include &lt;inttypes.h&gt;

#include &lt;pthread.h&gt;

#include &lt;sys/neutrino.h&gt;

#include &lt;atomic.h&gt;

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

 uint64_t N = 100000;

 bool atom = false, value = false;

 int opt, val;

 while ((opt = getopt(argc, argv, &quot;n:av&quot;)) != -1) {

  switch(opt) {

  case 'n': // количество повторений

   if (sscanf(optarg, &quot;%i&quot;, &amp;val) != 1)

    cout &lt;&lt; &quot;parse command line error&quot; &lt;&lt; endl, exit(EXIT_FAILURE);

   if (val &gt; 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(&amp;mutex);

   ind++;

   pthread_mutex_unlock(&amp;mutex);

  }

 } else if (value)

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

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

 t = ClockCycles() - t;

 cout &lt;&lt; &quot;all cycles - &quot; &lt;&lt; t &lt;&lt; &quot;; on operation - &quot;

  &lt;&lt; t / N &lt;&lt; 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 ... 65 66 67 68 69 70 71 72 73 ... 106
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу QNX/UNIX: Анатомия параллелизма - Цилюрик Олег Иванович бесплатно.
Комментарии
Открыть боковую панель
Комментарии
Лариса
Лариса 11.03.2026 - 20:17
Странно,здесь имя девочки Аграфена,а на других сайтах я прослушала три книги этого цикла с именем Дарвина.Зачем менять имя и путать читателей? Опечатка- не Дарвина,  а  Дарина.
Григорий
Григорий 09.04.2025 - 22:24
Лучшая книга всех времен
Женя
Женя 02.04.2025 - 16:08
Любимая книга