Comments 39
std::atomic
разве не решил бы проблему?Сам себе отвечу: решил бы. Автор оригинального кода об этом знает, но специально использует
memory_order_relaxed
, чтобы показать, как оно может глючить.Решил бы, конечно. Если он существует. Применительно к данному случаю, Objective C — надстройка над С.
А что тут от Objective C?
Кстати, процессор, на котором исполняется код. В теории, Objective-C должен обладать той же проблемой, если Apple не принял каких-то дополнительных усилий.
Но в любом случае, инструментарий довольно широк:
@synchronized
GCD
pthread_*_lock.
Будет время — оттестирую.
Пс. ой, какую археологию я устроил :D
Но в любом случае, инструментарий довольно широк:
@synchronized
GCD
pthread_*_lock.
Будет время — оттестирую.
Пс. ой, какую археологию я устроил :D
В коде он и используется
UFO just landed and posted this here
Дык std::atomic и используется.
Вот семейка методов
en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
вот возможные параметры-энумы
en.cppreference.com/w/cpp/atomic/memory_order
Вот семейка методов
en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
вот возможные параметры-энумы
en.cppreference.com/w/cpp/atomic/memory_order
«Сейчас мы впервые столкнулись с массовым использованием многоядерных ARM-процессоров. Раньше они были только в серверах или в высокопроизводительных «маках» прошлого на PowerPC.»
Вы ничего не перепутали? ARM и PowerPC — совершенно разные архитектуры.
Вы ничего не перепутали? ARM и PowerPC — совершенно разные архитектуры.
Да, просто неточно выразился, исправил, tnx.
Ну и, кстати, процессоры Intel поддерживают и активно используют out-of-order execution начиная с первых Pentium Pro. И барьеры памяти там точно так же применяются. Даже в GCC есть instrinic'и типа __sync_synchronize(). Вот цитата:
«Throughout the 1990s out-of-order execution became more common, and was featured in the IBM/Motorola PowerPC 601 (1993), Fujitsu/HAL SPARC64 (1995), Intel Pentium Pro (1995), MIPS R10000 (1996), HP PA-8000 (1996), AMD K5 (1996) and DEC Alpha 21264 (1998). Notable exceptions to this trend include the Sun UltraSPARC, HP/Intel Itanium, Transmeta Crusoe, Intel Atom, and the IBM POWER6.»
en.wikipedia.org/wiki/Out-of-order_execution
«Throughout the 1990s out-of-order execution became more common, and was featured in the IBM/Motorola PowerPC 601 (1993), Fujitsu/HAL SPARC64 (1995), Intel Pentium Pro (1995), MIPS R10000 (1996), HP PA-8000 (1996), AMD K5 (1996) and DEC Alpha 21264 (1998). Notable exceptions to this trend include the Sun UltraSPARC, HP/Intel Itanium, Transmeta Crusoe, Intel Atom, and the IBM POWER6.»
en.wikipedia.org/wiki/Out-of-order_execution
> Вы ничего не перепутали? ARM и PowerPC — совершенно разные архитектуры.
В контексте данной статьи автор мог смело писать их в одном предложении: модели памяти этих архитектур близнецы-братья.
В контексте данной статьи автор мог смело писать их в одном предложении: модели памяти этих архитектур близнецы-братья.
ARM — родоначальник PowerPC
Статья полезна как просветительская для тех, кто хочет разобраться в сути вещей, но прикладным программистам я бы рекомендовал просто использовать системные средства синхронизации или хорошо зарекомендовавшие себя библиотеки, написанные знающими тонкости людьми. Нужно заметить, что описанные проблемы возникают при использовании своих, безграмотно написанных реализаций mutex. Системные реализации mutex содержат в своем коде инструкции memory barriers (или другие средства синхронизации). Прикладной программист с высокой вероятностью все равно запутается во всем многообразии различных средств синхронизации на разных процессорах. Кроме out-of-order load/store на ARM есть куча других заморочек на других процессорах: протоколы кэшей, не гарантирующие консистентность, необходимость сбрасывать очередь инструкций, всякая aсquire/release логика на Itanium, и прочая низкоуровневая фигня, которую целиком прикладной программист не охватит если захочет писать программы на C/C++ под широкий спектр архитектур.
UFO just landed and posted this here
В C++ этот тоже есть.
Да, это очень хорошая спецификация. Отдельно упомяну страницу The JSR-133 Cookbook for Compiler Writers на которой Doug Lea (кстати автор знаменитого аллокатора памяти) систематизировал данные по распространенным архитектурам CPU в плане поддержки на них memory barriers и out-of-order load/store.
И что? Там запрещеены только store-store перестановки. На остальном можно спокойно огребать. Здесь как раз огребли на load-store или strore-load перестановке. В джаве нет припятствий к этому.
UFO just landed and posted this here
1) Во первых тут написано не не любой field, а только volatile!
2) Во вторых тут написано про same volatile, а не про перестановку чтения и записи разных полей.
На хабре была хорошая статья про все эти штучки в джаве, почтитайте.
Или у меня можете взглянуть, но там про .NET. В .NETе модель чуть построже.
2) Во вторых тут написано про same volatile, а не про перестановку чтения и записи разных полей.
На хабре была хорошая статья про все эти штучки в джаве, почтитайте.
Или у меня можете взглянуть, но там про .NET. В .NETе модель чуть построже.
Это std::mutex, — это еще ничего. Блокирующий объект синхронизации.
А если чуваки решили неблокирующие lock-free алгоритмы сделать на атомарных операциях?
Берут
en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
нихрена это en.cppreference.com/w/cpp/atomic/memory_order не прочитали, да еще спецом изменили этот параметр и начинаются фокусы.
В стандартную библиотеку эти аргументы добавили видать для большей гибкости.
Если order аргумент в bool compare_exchange_strong( T& expected, T desired, std::memory_order order = td::memory_order_seq_cst ); не трогать, то по умолчанию будет memory_order_seq_cst.
Нехрен трогать этот параметр, если неискушен и не знаешь к чему это приведет.
А если чуваки решили неблокирующие lock-free алгоритмы сделать на атомарных операциях?
Берут
en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
нихрена это en.cppreference.com/w/cpp/atomic/memory_order не прочитали, да еще спецом изменили этот параметр и начинаются фокусы.
В стандартную библиотеку эти аргументы добавили видать для большей гибкости.
Если order аргумент в bool compare_exchange_strong( T& expected, T desired, std::memory_order order = td::memory_order_seq_cst ); не трогать, то по умолчанию будет memory_order_seq_cst.
Нехрен трогать этот параметр, если неискушен и не знаешь к чему это приведет.
Автор молодец, создал проблему на пустом месте и теперь всем расказывает, что она есть.
1. Если не понимаешь зачем нужны и как пользоваться memory_order_acquire и иже с ними — не используй. По умолчанию все atomic операции в C++11 используют последовательную согласованность. Что даёт гарантию выполнения кода так, как он написан.
2. Код автора не верен.
compare_exchange_strong — read-write операция. Почему он использует только acquire? Допустим мы имеем два потока, один сделал exchange(сиречь release), другой читает(acquire). Но тип операции указан как acquire, а значит этот release не будет синзронизирован с acquire. Поэтому код должен выглядеть как:
3. В большинстве случаев надо использовать std::mutex и собратьев дабы не иметь никакой головной боли.
1. Если не понимаешь зачем нужны и как пользоваться memory_order_acquire и иже с ними — не используй. По умолчанию все atomic операции в C++11 используют последовательную согласованность. Что даёт гарантию выполнения кода так, как он написан.
2. Код автора не верен.
if (flag.compare_exchange_strong(expected, 1, memory_order_acquire))
{
// The lock succeeded
}
compare_exchange_strong — read-write операция. Почему он использует только acquire? Допустим мы имеем два потока, один сделал exchange(сиречь release), другой читает(acquire). Но тип операции указан как acquire, а значит этот release не будет синзронизирован с acquire. Поэтому код должен выглядеть как:
if (flag.compare_exchange_strong(expected, 1, memory_order_acq_rel))
{
// The lock succeeded
}
3. В большинстве случаев надо использовать std::mutex и собратьев дабы не иметь никакой головной боли.
По пункту 2 не совсем так. Тут не идет речь ни о какой синхронизации. memory_order_acquire просто гарантирует, что все инструкции, стоящии после барьера не будут переупорчдоченты так, что окажутся до барьера.
В данном случае это гарантирует, что никто не прочтет переменные до получения мьютекса — этого достаоточно для корректной работы.
memory_order_acquire гарантирует противоположную вещь — что инструкции стоящии до барьера не запрыгнут после барьера. Здесь это абсолютно пофиг.
В данном случае это гарантирует, что никто не прочтет переменные до получения мьютекса — этого достаоточно для корректной работы.
memory_order_acquire гарантирует противоположную вещь — что инструкции стоящии до барьера не запрыгнут после барьера. Здесь это абсолютно пофиг.
Была подобная статья: habrahabr.ru/post/63697/
UFO just landed and posted this here
В принципе, проверить можно. Только это очень дорого: на одну строчку кода придётся написать примерно строчек 30 спецификации и доказательств. Есть seL4, для которой эта титаническая работы была проделана. Но, в принципе, да, Вы правы. Синхронизация — это дополнительные алгоритмы и их реализации в системе, где могут возникнуть ошибки. Чем меньше таких мест, тем лучше. Но иногда просто не хватает энергоэффективности, и, всё равно, приходится использовать многоядерные решения.
Как я понимаю, проверить любой код на отсутствие таких лаж невозможно.Это технически возможно, просто до поры до времени это не очень волновало даже самих производителей процессоров, но последние несколько лет ситация поменялась. Сейчас есть исследовательская группа в Кембридже (и сочувствующие в INRIA и прочих учреждениях), которая формализировала модели памяти SPARC, x86 и Power (раз и два). Есть люди, интересующиеся формальной верификацией поверх слабых моделей памяти (например, C++).
Почему-то глядя на эту тему, подумалось, что идет борьба разработчиков процессоров с программистами.
Мне вот просто интересно, если в процессоре изначально обойтись без этих оптимизаций с перестановками инструкций, то потом в программах можно будет обойтись без барьеров памяти, то есть мьютексы будут более просты и менее затратны.
Может это приведет к бОльшей производительности?
Мне вот просто интересно, если в процессоре изначально обойтись без этих оптимизаций с перестановками инструкций, то потом в программах можно будет обойтись без барьеров памяти, то есть мьютексы будут более просты и менее затратны.
Может это приведет к бОльшей производительности?
Sign up to leave a comment.
Демонстрация сбоев программы при отсутствии барьеров памяти