Шрифт:
Интервал:
Закладка:
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%):
- Как заработать в Интернете. 35 самых быстрых способов - Ольга Фомина - Интернет
- Wi-Fi: Все, что Вы хотели знать, но боялись спросить - А. Щербаков - Интернет
- SEO для бизнеса - Гроховский Леонид - Интернет
- Wiki-правительство: Как технологии могут сделать власть лучше, демократию – сильнее, а граждан – влиятельнее - Бет Новек - Интернет
- Как сделать сайт адаптивным: полезные советы - "TemplateMonster" - Интернет
- Деловая e-mail переписка. Пять правил успеха - Тамара Воротынцева - Интернет
- Рунетология. Кто управляет русским Интернетом? - Максим Спиридонов - Интернет
- Галактика Интернет - Мануэль Кастельс - Интернет
- Skype: бесплатные звонки через Интернет. Начали! - Виктор Гольцман - Интернет
- Электронные деньги. Интернет-платежи - Михаил Мамута - Интернет