Поточно-ориентированные атомарные операции в gcc

В программе, над которой я работаю, у меня много следующего кода:

pthread_mutex_lock( &frame->mutex );
frame->variable = variable;
pthread_mutex_unlock( &frame->mutex );

Очевидно, что это пустая трата циклов ЦП, если среднюю инструкцию можно просто заменить атомарным хранилищем. Я знаю, что gcc вполне на это способен, но мне не удалось найти много документации по таким простым потокобезопасным атомарным операциям. Как мне заменить этот набор кода атомарной операцией?

(Я знаю, что простые хранилища теоретически должны быть атомарными, но я не хочу надеяться, что оптимизатор не испортит их атомарность в какой-то момент процесса.)

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

Начиная с ядра 2.6, стоимость мьютекса почти равна нулю, когда мьютекс свободен. В любом случае, «__sync_lock_test_and_set» (начиная с gcc 4.1) должен помочь, это не единственная функция, которую можно использовать в этом случае.

claf 08.03.2013 16:34

Кстати, __atomic_store или __atomic_store_n кажется более подходящим.

claf 09.03.2013 20:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
17
2
20 875
5

Ответы 5

Вы можете проверить документацию gcc. Для текущей версии gcc (4.3.2) это будет глава 5.47 Встроенные функции для доступа к атомарной памяти - для других версий gcc, пожалуйста, проверьте свою документацию. Это должно быть в главе 5 - Расширения семейства языков C.

Между прочим, компилятор C не дает никаких гарантий относительно того, что простые операции хранения являются атомарными. Вы не можете полагаться на это предположение. Чтобы машинный код операции выполнялся атомарно, ему нужен префикс LOCK.

Более того, эти встроенные функции для доступа к атомарной памяти также поддерживаются ICC (в случае, если вы собираетесь переносить компилятор). Я думаю, что для GCC атомарные функции поддерживаются начиная с версии 4.1, поэтому обязательно используйте #ifdef для обеспечения версии gcc или icc!

claf 04.05.2009 15:42

Значит, в случае одновременного доступа __sync_add_and_fetch ненадежен? и мы должны использовать lock addx напрямую с встраиванием сборки?

Bionix1441 27.05.2015 10:02

AFAIK, вы не можете префикс инструкций MOV с помощью LOCK; это разрешено только для операций RMW. Но если он делает использует простое хранилище, ему также может понадобиться барьер памяти, который неявно связан с мьютексом, а также с инструкциями, разрешающими LOCK.

На x86 и большинстве других архитектур выровненные 4-байтовые операции чтения и записи всегда атомарны. Однако оптимизатор может пропускать / переупорядочивать операции чтения и записи в одном потоке.

Что вы хотите сделать, так это сообщить компилятору, что другие потоки могли коснуться этой области памяти. (Побочный эффект pthread_mutex_lock сообщает компилятору, что другие потоки могли затронуть любую часть памяти.) Вы можете увидеть, что volatile рекомендуется, но этого нет в спецификации C, и GCC не интерпретирует volatile таким образом.

asm("" : "=m" (variable));
frame->variable = variable;

- это специфичный для GCC механизм, говорящий, что «variable был записан, перезагрузите его».

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

ephemient 05.10.2008 02:24

До определенного момента атомарные операции в C предоставлялись прямо из исходников ядра через заголовок atomic.h.

Однако использование заголовков ядра непосредственно в коде пользовательского пространства - очень плохая практика, поэтому файл заголовка atomic.h был удален некоторое время назад. Вместо этого мы можем теперь использовать «атомарные встроенные модули GCC», которые являются гораздо лучшим и более надежным подходом.

Есть очень хорошее объяснение, предоставленное Тудором Голубенко в его блоге. Он даже обеспечивает замену исходного файла atomic.h на случай, если у вас есть какой-то код, который в этом нуждается.

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

Как я вижу, вы используете платформу GNU для разработки, поэтому можно с уверенностью сказать, что glic предоставляет тип данных int с атомарными возможностями, 'sig_atomic_t'. Таким образом, этот подход может гарантировать вам атомарные операции на уровне ядра. не уровни gcc.

sig_atomic_t является атомарным только по отношению к сигналам. Это потокобезопасный нет.

Henri Menke 18.03.2019 04:31

Другие вопросы по теме