Что значит «сильно случается раньше»?

9

Фраза «сильно случается раньше» используется несколько раз в проекте стандарта C ++.

Например: Завершение [basic.start.term] / 5

Если завершение инициализации объекта со статической продолжительностью хранения сильно происходит до вызова std :: atexit (см. [Support.start.term]), вызов функции передается в std :: atexit последовательность перед вызовом деструктора для объекта. Если вызов std :: atexit сильно происходит до завершения инициализации объекта со статической продолжительностью хранения, то вызов деструктора для объекта упорядочивается до того, как вызов функции передается в std :: atexit. , Если вызов std :: atexit сильно происходит перед другим вызовом std :: atexit, то вызов функции, переданной второму вызову std :: atexit, упорядочивается до вызова функции, переданной первый вызов std :: atexit.

И определено в гонках данных [intro.races] / 12

Оценка A сильно случается перед оценкой D, если либо

(12.1) A секвенируется перед D, или

(12.2) A синхронизируется с D, и A и D являются последовательно согласованными атомарными операциями ([atomics.order]), или

(12.3) существуют оценки B и C такие, что A секвенируется перед B, B просто происходит перед C, а C секвенируется перед D, или

(12.4) существует оценка B такая, что A сильно случается до B, а B сильно предшествует D.

[Примечание: неофициально, если A сильно случается до B, то A, кажется, оценивается перед B во всех контекстах. Сильно случается до того, как исключаются операции потребления. - конец примечания]

Почему было «сильно случается прежде» введено? Интуитивно понятно, в чем его отличие и связь с «случается раньше»?

Что означает «А, по-видимому, оценивается перед В во всех контекстах» в примечании?

(Примечание: мотивация для этого вопроса - комментарии Питера Кордеса под этим ответом .)

Дополнительный проект стандартной цитаты (спасибо Peter Cordes)

Порядок и последовательность [atomics.order] / 4

Для всех операций memory_order :: seq_cst, включая заборы, существует один общий порядок S, который удовлетворяет следующим ограничениям. Во-первых, если A и B являются операциями memory_order :: seq_cst и A сильно предшествует B, то A предшествует B в S. Во-вторых, для каждой пары атомарных операций A и B над объектом M, где A упорядочено по когерентности перед B следующие S должны выполнить следующие четыре условия:

(4.1) если A и B являются обеими операциями memory_order :: seq_cst, то A предшествует B в S; а также

(4.2) если A является операцией memory_order :: seq_cst и B происходит перед забором памяти memory_order :: seq_cst Y, то A предшествует Y в S; а также

(4.3) если ограничение memory_order :: seq_cst X происходит до того, как A и B является операцией memory_order :: seq_cst, то X предшествует B в S; а также

(4.4) если ограничение memory_order :: seq_cst X происходит до A и B происходит до забора memory_order :: seq_cst Y, то X предшествует Y в S.

curiousguy
источник
1
В текущем проекте стандарта также упоминается «Сильно случается перед В» в качестве условия для правила, применяемого seq_cstв Atomics 31.4 Порядок и последовательность: 4 . Этого нет в стандарте C ++ 17 n4659 , где 32.4-3 определяют существование единого общего порядка операций seq_cst, соответствующих порядку «происходит раньше» и порядкам модификации для всех затронутых местоположений ; «сильно» был добавлен в более поздний проект.
Питер Кордес
2
@PeterCordes Я думаю, что комментарий, исключающий потребление, констатацию его HB «во всех контекстах» / «сильный» и разговоров о вызовах указателей функций, является чем-то вроде мертвой раздачи. Если многопоточная программа вызывает atexit()в одном потоке и exit()в другом, инициализаторам недостаточно переносить только зависимость, основанную на потреблении, только потому, что результаты тогда отличаются от того, exit()был ли вызван тем же потоком. Мой более старый ответ касался этой разницы.
Iwillnotexist Idonotexist
@IwillnotexistIdonotexist Можете ли вы выйти из программы MT? Разве это не принципиально сломанная идея?
любопытный парень
1
@curiousguy Это цель exit(). Любой поток может убить всю программу, выйдя из системы, или основной поток может выйти, return-ing. Это приводит к вызову atexit()обработчиков и смерти всех потоков, что бы они ни делали.
Iwillnotexist Idonotexist

Ответы:

5

Почему было «сильно случается прежде» введено? Интуитивно понятно, в чем его отличие и связь с «случается раньше»?

Готовьтесь к тому, что «просто случается раньше»! Посмотрите на этот текущий снимок cppref https://en.cppreference.com/w/cpp/atomic/memory_order

введите описание изображения здесь

Кажется, «просто случается раньше» добавлено в C ++ 20.

Просто бывает раньше

Независимо от потоков, оценка A просто происходит до оценки B, если выполняется любое из следующих условий:

1) А секвенируется до B

2) А синхронизируется с Б

3) просто случается - до X, а X просто происходит - до B

Примечание: без операций потребления, просто происходит до и после того, как отношения одинаковы.

Так что Simply-HB и HB одинаковы, за исключением того, как они обрабатывают операции потребления. Смотрите HB

Случается, перед тем

Независимо от потоков оценка A происходит до оценки B, если выполняется любое из следующих условий:

1) А секвенируется до B

2) Межпотоковый процесс происходит до B

Реализация требуется для обеспечения того, чтобы отношение «происходит до» было ациклическим, путем введения дополнительной синхронизации, если это необходимо (это может быть необходимо, только если задействована операция потребления, см. Batty et al.)

Как они отличаются в отношении потребления? Смотрите Inter-Thread-HB

Меж-поток происходит раньше

Между потоками оценка A происходит между потоками перед оценкой B, если выполняется любое из следующих условий

1) А синхронизируется с Б

2) A упорядочен по зависимости перед B

3) ...

...

Операция, которая упорядочена по зависимости (то есть использует выпуск / потребление), является HB, но не обязательно просто-HB.

Потребление более расслаблено, чем приобретение, поэтому, если я правильно понимаю, HB более расслаблен, чем Simply-HB.

Сильно бывает - раньше

Независимо от потоков, оценка A строго выполняется до оценки B, если выполняется одно из следующих условий:

1) А секвенируется до B

2) A синхронизируется с B, и A и B являются последовательно согласованными атомарными операциями

3) A секвенируется - до X, X просто происходит - до Y, а Y - до B

4) Сильно происходит - до X, а X сильно случается - до B

Примечание: неофициально, если A сильно случается - перед B, тогда A, кажется, оценивается перед B во всех контекстах.

Примечание: сильно случается, прежде чем исключить операции потребления.

Таким образом, операция освобождения / потребления не может быть строго-HB.

Освобождение / приобретение может быть HB и Simply-HB (потому что релиз / приобретение синхронизируется с), но не обязательно строго-HB. Потому что Strongly-HB, в частности, говорит, что A должен синхронизироваться с B, и быть последовательной последовательной операцией.

                            Is happens-before guaranteed?

                        HB             Simply-HB          Strongly-HB

relaxed                 no                 no                 no
release/consume        yes                 no                 no      
release/acquire        yes                yes                 no
S.C.                   yes                yes                yes

Что означает «А, по-видимому, оценивается перед В во всех контекстах» в примечании?

Все контексты: все потоки / все процессоры видят (или «в конечном итоге согласятся») один и тот же порядок. Это является гарантией последовательной согласованности - глобального общего порядка изменения всех переменных. Цепочки получения / освобождения гарантируют только предполагаемый порядок модификации для потоков, участвующих в цепочке. Нити вне цепочки теоретически могут видеть другой порядок.

Я не знаю, почему были введены Strongly-HB и Simply-HB. Может быть, чтобы помочь уточнить, как работать вокруг потреблять? Сильно-HB обладает хорошими свойствами - если один поток наблюдает за A-случаем-до-B, он знает, что все потоки будут наблюдать одно и то же.

История потребления:

Пол Э. МакКенни отвечает за то, чтобы потребители были в стандартах C и C ++. Потребление гарантирует порядок между назначением указателя и памятью, на которую он указывает. Это было изобретено из-за DEC Alpha. DEC Alpha может умело разыменовывать указатель, поэтому он также имеет забор памяти для предотвращения этого. DEC Alpha больше не производится, и ни у одного из современных процессоров такого поведения нет. Потребление предназначено, чтобы быть очень расслабленным.

Хамфри Виннебаго
источник
1
Печаль во благо. Я почти жалею, что задаю этот вопрос. Я хочу вернуться к решению простых проблем C ++, таких как правила аннулирования итераторов, поиск имени в зависимости от аргумента, определяемые пользователем операторы преобразования, вывод аргумента шаблона, когда поиск имени выполняется в базовом классе в элементе шаблона, и когда вы можно преобразовать в виртуальную базу в начале строительства объекта.
любопытный парень
Re: потреблять. Вы утверждаете, что судьба заказа потребителя связана с судьбой DEC Alpha и не имеет значения вне этой конкретной арки?
любопытный парень
1
Это хороший вопрос. Если взглянуть на это подробнее, то кажется, что потребление может теоретически повысить производительность для слабо упорядоченных арок, таких как ARM и PowerPC. Дай мне еще немного времени, чтобы разобраться в этом.
Хамфри Виннебаго
1
Я бы сказал, что потребление существует из-за всех слабо упорядоченных ISA, кроме Alpha. В Alpha Asm единственные параметры смягчены и приобретают (и seq-cst), а не упорядочение зависимостей. mo_consumeпредназначена для использования преимуществ упорядочения зависимости данных на реальных процессорах и формализации того, что компилятор не может нарушить зависимость данных с помощью прогнозирования ветвлений. Например, int *p = load(); tmp = *p;может быть нарушен введением компилятора, if(p==known_address) tmp = *known_address; else tmp=*p;если у него есть основания ожидать, что определенное значение указателя будет общим. Это законно для расслабленного, но не потреблять.
Питер Кордес
@PeterCordes right ... арки со слабым упорядочением должны создавать барьер памяти для получения, но (теоретически) не для потребления. Похоже, вы думаете, что если бы Альфа никогда не существовала, мы все равно потребляли бы? Кроме того, вы в основном говорите, что потребление - это причудливый (или «стандартный») барьер компилятора.
Хамфри Виннебаго