Pull to refresh

Comments 33

Можно еще было упомянуть какие атоматные операции поддерживает тот же gcc начиная еще с версии 4.1.1. Правда не для всех целевых архитектур.
В новом стандарте C++11 я кажется слышал определены атомарные операции. Верно?
> Кстати, инкремент, декремент, +=, -= в С++ атомарные (по крайней мере на х86 и х86_64).

Нет. Поскольку «одна ассемблерная комманда»!=«одна операция».

UFO just landed and posted this here
Написал в статье «Double-Word Compare-And-Swap (часто путается с Double Compare And Swap).» и сам перепутал. Код конечно относится к Double-Word Compare-And-Swap, а не Double Compare And Swap(DCAS). Спасибо, исправил.
UFO just landed and posted this here
Шину доступа памяти я взял как самый близкий перевод текстов из нескольких источников:
«The lock instruction ensures that no one else can fiddle with the memory while the next instruction executes.» и
«An operation during which a processor can simultaneously read a location and write it in the same bus operation. Bus — A collection of wires through which data is transmitted from one part of a computer to another.» «Expansion bus that enables expansion boards to access the CPU and memory.» Я не смог удовлетворительно перевести bus. Возможно, я неверно понял эти строки.
UFO just landed and posted this here
> Шину доступа памяти

Да нет уже в современных процессорах и материнских платах никаких шин с общим доступом! Там везде выделенные point-to-point линки: QPI, PCI Express — всё это выделенные point-to-point линки, на каждом из которых ровно два устройства, обменивающиеся данными. Блокировать такие линки от доступа кем-то ещё нет никакого смысла, так как там больше никого и нет.
UFO just landed and posted this here
вполне возможно сделать одновременно несколько LL.

Если про несколько LL подряд без соответствующих SC, то вики с Вами не согласна:
Any exceptional events between the two operations, such as a context switch, another load-link, or even (on many platforms) another load or store operation, will cause the store-conditional to spuriously fail.
Насчет лока: плохо написал, сейчас исправлю.
UFO just landed and posted this here
Real implementations of LL/SC do not always succeed if there are no concurrent updates to the memory location in question (с) Вики. Если в разных потоках сделать LL на одну и ту же переменную, то они сфейлятся. Если на несвязанные участки памяти, то с ними ничего не будет(хотя тут вики говорит, что не всегда). Я просто не понял, что именно подразумевалось в «сделать одновременно несколько LL», поэтому и уточнил.
UFO just landed and posted this here
Насколько я понимаю, единственное, что гарантирует fail SC — это что новое значение операнда не будет записано. Если же между LL и SC изменялись другие переменные, то они и не откатятся.
UFO just landed and posted this here
Согласен, там нужно использовать не «данные», а «операнд». Т. к. механизм LL/SC распространяется только на него, а не на все данные. Спасибо, исправил.
UFO just landed and posted this here
0. Если уж превращать комментарии к топику в «коллекцию отрывков знаний», то пожалуй внесу свой посильный вклад:

1. Во-первых, имхо, читателям станет намного понятнее, если сказать, что «Атомарные Операции» можно разделить на два с половиной вида:

1) Средства дающие потоку механизм для обнаружения факта модификации данных другим потоком.
2) Средства дающие возможность «монопольного захвата» ячейки памяти в момент модификации.
3) Неявные средства

Операции 1-го вида присутствуют на всех современных архитектурах, предусматривающих «многоядерность». Это LL/SC в RISC и CMPXCHG в X86.

Операции 2-го вида есть ТОЛЬКО в таких архитектурах (как X86), которым «в качестве наследства» досталось обилие комманд Чтение-Модификация-Запись в системе комманд. (Т.е. наличие memory-операнда назначения в арифметико-логических операциях.) В архитектурах типа ARM таких комманд просто нет (там только считал в регистр — что-то сделал — записал).

Операция 3-го вида — это операция обмена регистр<—>ячейка. Таки да, в тех архитектурах, где таковая «ассемблерная инструкция» имеет место присутствовать, процессор обычно «прозрачно» предпринимает «меры» (или же явно предлагает средства) для того, чтобы в сам момент обмена не вмешался другой процессор.

2. Программистов решивших применить «Атомарные Операции» — ждет несколько «ловушек» со стороны… оптимизирующего компилятора.

Ловушка #1) Примеры ассемблерных вставок выше, у которых отсутствует asm(:::«memory») — это потенциальный источник бага. Как вы думаете, что будет, что «может взбрести в голову компилятору» если вы не только «в тупую» «инкрементируете/декрементируете» ячейку но и «прочитали» до или после это значение из этой ячейки?
Добрый совет (насчет «memory»): А вообще — если вы из ассемблерной вставки решили «насрать» куда-нибудь в тайне от компилятора, будь то регистр или память — готовьтесь к неприятностям!

Ловушка #2) Косвенно связана с первым пунктом. А именно, если вы «Атомарные Операции» пытаетесь использовать, например, для того, чтобы смену какого-нибудь указателя в структуре сделать потокобезопасной. Атомарные операции конечно хорошо работают с «памятью» будь то ram-память или синхронизируемая многоуровневая система кешей в smp-системе, но они не относятся к РЕГИСТРАМ CPU. А именно в регистр cpu «покладет» компилятор значение вашего указателя и именно как значение регистра он будет использоваться. Поэтому, во-первых, не забываем про «volatile», а во-вторых лучше чтобы подобные значения возвращались из ассемблерных вставок.

3. Атомарные операции конечно хорошо. Однако далеко не всегда они в чистом виде панацея. Готовьтесь к тому, чтобы «грамотно расставить» барьеры чтения/записи.
Нужно еще упомянуть, что отладка ошибок вида «гонки сигналов» — самое сложное, что есть вообще в программировании.
Поэтому изучить ассемблерные примитивы конечно познавательно, но в реальных проектах используйте только штатные механизмы языка или ОС:)

Ваш кэп.
Наш Кэп только что забыл упомянуть, что как раз именно в «реальных helloworldhighload-проектах» и можно легко получить где-то так 1000...1000000 кратный «профит в перфомансе», при замене оверхеда ненужного переключения задач (возникающего, например, когда несколько потоков «жопами толкаются» на одном бездумно поставленном мутексе) на копеечную атомарную операцию «весь оверхед» которой несколько наносекунд (десяток машинных тактов в худшем случае).

Да, ничего не дается бесплатно, а «знание особенно».

… и при этом из-за гонок потерять 0.00001% записей о движении ресурсов (вокруг которых чаще всего атомики и строятся). Будь то сотни нефти в игре или сотни тыщь мильонов в банке.
Будете потом на свежем сибирском воздухе рассказывать собратьям по лесоповалу про мильонократный профит в перфомансе.
В реальных системах, про которые я говорю, во-первых оптимизируются только _реально_ узкие места (про принцип Парето 20/80 вы наверное слышали), и неумеренный перфекционизм обычно имеет правильную тенденцию жестко пресекаться (проверенные куски кода работают по принципу «работает-не трогай» пока на них или на тот кусок что формирует для них данные явно не укажет профайлер). Про то, что все те енхансменты, которые прямо или косвенно могут что-то обрушить, проходят очень жесткие тесты перед выкатом на продакшен.

Во-вторых, насчет «тыщ милионов» которыми вы меня пытаетесь «напугать» — если у вас в системе претендующей на роль «серьезного бизнеса» все «навалено в кучу» без жесткого разделения по серверам и зонам отвественности (финансовая часть, платежная, управленческая, интерфейсная, массивная обработка данных, контрольная, секурити-часть, часть внешнего автоматического взаимодействия и т.д.) то это хреновая архитектура информсистемы (и в данном случае в такой хреновой неизолированной архитектуре, «сотни тыщ мильонов» убытка легко вызовет и криворукий программист-стажер например «впиливавший» кнопочку «в формочку» но ошибшийся в названии метода). В хреновой архитектуре (например «стремные» финансовые проводки «на тыщи мильонов» не отлавливаются сразу при возникновении и не отправляются «на разбор полетов» в СБ) можно потерять и «100%» записей и без всяких гонок!

Поэтому мне пожалуйста не надо, рассказывать «ужасные истории», я вам сам могу при желании рассказать многое чего… «Обезьяна с гранатой» в виде программиста, который вообще не имеет представление о «мультипоточных системах», при этом не хочет даже иметь представление (считая, например, что «обо всем подумает умный компилятор») это, поверьте, намного хуже!!! Кстати, как показывает моя практика, те программисты которые не понимают и не стремятся понимать «атомарные примитивы» они… внезапно точно также не умеют и не хотят понимать понимать также и «штатные механизмы языка или ОС» на которых вы так настаиваете! (Грузящий систему мутекс ради самого мутекса, или ничего и не от чего не защищающий семафор ради семафора — вот весь продукт, за который я многократно лично «бил по рукам»). И еще один обратный парадокс — те программисты, которые поняли «низкий уровень» — они как раз (как и я сам) без особой необходимости «в нижние миры» не лазят, в при возможности используют как раз «штатные средства», но если уже «припрет» — так по крайней мере точно не обрушат систему «атомиком».
Что вы, собственно, пытаетесь доказать? Что ленивый быдлокодер сделает хуже и медленнее супер-про-аса? Кто ж с этим спорит?
Но если взять двух равноценных асов, один из которых к месту использует стандартные примитивы, а второй фигачит всё сам на инлайновом асме? Ну я бы второго и близко не подпустил к написанию реальной системы — он будет работать долго, дорого и глючно. И когда он всё наконец отладит, как раз выйдет следующее поколение железа и софта, которое вскроет свежие баги. Разруливать дедлоки вы тоже на асме будете?
Синхронизация многопоточки — не та область, куда можно лезть со своими велосипедами.
Во-первых, если вы заметили, я ни кого не призывал «весь код переписывать на инлайн-асме». Кстати, я лично ни разу не видел ни одного такого чудо-программиста который бы на инлайн-асме бы «фигачил всё».

Во-вторых, с чем я сразу не согласился — так это с вашим тезисом «синхронизация — это слишком сложно, а синхронизация с помощью атомарных примитивов — еще сложнее, поэтому не просто суйтесь туда» (я ведь правильно понял ваш Тезис, поправьте если нет).

В-третьих, собственно мой антитезис — нормальный программист как раз должен знать как «синхронизация многопоточки» устроена внутри и причем хорошо знать. Знать, даже если ему самому и не придется «разруливать дедлоки на асме». Причем я утверждаю, что программист, который не знает «матчасть» как и почему возникают «гонки» в многопоточных системах просто не способен правильно использовать и «высокоуровневые вещи». Чтобы от чего-то защитится надо знать от чего защищатmся.

В-четвертых, насчет «профита» и «рисков». Я не зря упомянул про «принцип Парето». Если профайлер показывает, что 98% времени потоки проводят «наяривая» один мутекс, согласитесь игнорировать этот факт просто неразумно. Да, я согласен, это «знание для продвинутых», но в данном случае… «быдлокодеры» могут даже не знать о существовании такого инструмента как «профайлер»;)

Ну, а в-пятых, я лично считаю, что тема достаточно серьезная и важная чтобы от нее просто «отмахиваться» ссылаясь на «сложность». Если «отбросить весь туман», то как раз все раскладывается по полочкам.

В-шестых, кстати «велосипедов»-то про которые вы говорите — как раз и нет никаких!!! Все «типовые операции» при применения «атомиков» уже успешно откатаны в самых высоконагруженных местах нашей планеты, все их плюсы и минусы изученны вдоль и поперек, а еще их можно чуть ли не по пальцам пересчитать… но при этом, к величайшему сожалению «из-за тумана» который нагоняет «сложность темы» это все не является достоянием широкой общественности. Многие «слышали звон», но не знают где он! А итог, простите, плачевный — при «миллионах» читателей английской википедии, ни одна, простите c$ка, не обратила внимание на то что в примерах в листинге есть потенциальный баг!

Собственно, да, тема «не из легких», но это не повод ее «замалчивать».
Ну замалчивать не повод конечно, вот только информации в этом посте совершенно недостаточно, чтобы разрулить предложенную вами ситуацию про 98% времени на один мютекс. Вот просто взять и заменить на какой-нибудь атомик? Ну-ну.
Я бы в такой ситуации сначала крепко подумал над логикой приложения, потом бы попробовал применить какую-нибудь неблокирующую структуру данных. Но это совершенно стандартный путь решения проблем с производительностью — сначала проверяем логику (нужно ли вообще здесь это вычисление), затем проверяем структуры данных и обрабатывающие их алгоритмы, и только в крайнем случае достаем «напильник» и лезем на низкий уровень. Безотносительно того, многопоточный это затык или нет.
Можете порекомендовать почитать что-то на тему «с гарантией правильного понимания»?
Вы бы написали что такое атомарные операции перед катом. Просто не все знают что это, и не всем это нужно
Гн Herb Sutter в своем блоге много пишет про многопоточность, атомики, и все это в свете c++11. А что делать тем, кто по каким-либо причинам не может перейти на c++11, и вынужден пользоваться c++ — может быть есть публично доступные библиотеки/врапперы, которые реализуют атомики по аналогии с std::atomic?
Слышал только про Boost Atomic.
Sign up to leave a comment.

Articles

Change theme settings