Що означає "сильно відбувається раніше"?


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 call.

І визначено у перегонах даних [intro.races] / 12

Оцінка A рішуче відбувається перед оцінкою D, якщо будь-яка

(12.1) A секвенується перед D, або

(12.2) Синхронізація з 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 у всіх контекстах. Сильно відбувається, перш ніж виключати споживачі. - кінцева примітка]

Чому було введено "сильно сталося раніше"? Інтуїтивно, в чому його відмінність та відношення до «буває раніше»?

Що означає "А, як видається, оцінюється перед B у всіх контекстах" у примітці?

(Зауважте: мотивацією до цього питання є коментарі Пітера Кордеса під цією відповіддю .)

Додатковий проект стандартної цитати (спасибі Пітеру Кордесу)

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

Є єдиний загальний порядок S у всіх операціях memory_order :: seq_cst, включаючи огорожі, що задовольняє наступним обмеженням. По-перше, якщо 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 - операція пам'яті_order :: seq_cst і B відбувається перед пам'яткою_порядок :: seq_cst огорожею Y, то A передує Y в S; і

(4.3) якщо огорожа memory_order :: seq_cst X відбувається перед тим, як A і B є операцією memory_order :: seq_cst, то X передує B у S; і

(4.4) якщо огорожа пам'яті :: seq_cst X ставиться до того, як А і В трапляється перед пам'яткою_порядок :: seq_cst паркан Y, то X передує Y у S


1
У чинному проектному стандарті також згадується "Сильно відбувається перед В" як умова для правила, що подає заявку seq_cst, в атоміці 31.4 Порядок та послідовність: 4 . Це не стосується стандарту C ++ 17 n4659 , де 32.4 - 3 визначає існування єдиного загального порядку seq_cst ops, що відповідає порядку "відбувається раніше" та замовленням на модифікацію для всіх постраждалих місць ; "сильно" було додано в більш пізньому проекті.
Пітер Кордес

2
@PeterCordes Я думаю, що коментар, що виключає споживання, заявляє, що це HB "у будь-якому контексті" / "сильний", і говорити про дзвінки до функціональних покажчиків - щось мертве. Якщо багатопотокова програма викликає atexit()один потік і exit()інший, для ініціалізаторів недостатньо лише залежність на основі споживання лише тому, що результати відрізняються від того, якщо exit()викликали один потік. Старше відповідь мого стурбований ця різниця.
Iwillnotexist Idonotexist


@IwillnotexistIdonotexist Чи можете ви навіть вийти з програми MT? Чи не принципово це зламана ідея?
curiousguy

1
@curiousguy Це мета exit(). Будь-який потік може знищити всю програму, вийшовши, або основний потік може вийти з return-ing. Це призводить до виклику atexit()обробників і загибелі всіх потоків, що б вони не робили.
Iwillnotexist Idonotexist

Відповіді:


5

Чому було введено "сильно сталося раніше"? Інтуїтивно, в чому його відмінність та відношення до «буває раніше»?

Підготуйте себе і для "просто буває раніше"! Подивіться на цей поточний знімок cppref https://en.cppreference.com/w/cpp/atomic/memory_order

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

Здається, що "просто буває раніше" додано в C ++ 20.

Просто буває-раніше

Незалежно від потоків, оцінка A просто відбувається перед оцінкою B, якщо щось із наступного:

1) A відслідковується перед B

2) Синхронізується-з B

3) А просто відбувається - перед X, а X просто - перед B

Примітка: без операцій споживання просто відбувається - перед і відбувається - перед відносинами однакові.

Таким чином, Simply-HB і HB однакові за винятком того, як вони обробляють операції з споживанням. Див. НВ

Трапляється-раніше

Незалежно від потоків, оцінка A відбувається перед оцінкою B, якщо відповідає дійсності щось з наступного:

1) A відслідковується перед B

2) Між потоком трапляється перед B

Реалізація потрібна для того, щоб відношення "раніше" було ациклічним, вводячи при необхідності додаткову синхронізацію (це може бути необхідно лише за участі операції споживання, див. Batty et al)

Чим вони відрізняються за споживанням? Див. Inter-Thread-HB

Міжпотокова трапляється раніше

Між потоками, оцінка Оцінка між потоками відбувається перед оцінкою B, якщо щось із наведеного нижче відповідає дійсності

1) Синхронізується з B

2) A - залежність, упорядкована перед B

3) ...

...

Операцією, яка впорядкована залежність (тобто використовує випуск / споживання), є HB, але не обов'язково Simply-HB.

Споживання є більш розслабленим, ніж придбання, тому, якщо я правильно зрозумів, НВ є більш розслабленим, ніж просто-НВ.

Сильно буває-раніше

Незалежно від потоків, оцінка A настійно відбувається перед оцінкою B, якщо щось із наступного:

1) A відслідковується перед B

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

3) A секвенується перед X, X просто відбувається - перед Y, а Y - перед B

4) Сильно буває - до Х, а Х сильно - перед В

Примітка: неофіційно, якщо A сильно стається перед B, то, схоже, A оцінюється перед B у всіх контекстах.

Примітка. Настійно відбувається, перш ніж виключати споживачі.

Тому операція з випуском / споживанням не може бути сильно-НВ.

Випуск / придбання може бути HB і Simply-HB (тому що випуск / придбання синхронізується з), але це не обов'язково сильно-HB. Оскільки сильно-НВ конкретно говорить, що A повинен синхронізуватися-з B AND бути послідовно послідовною операцією.

                            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

Що означає "А, як видається, оцінюється перед B у всіх контекстах" у примітці?

Усі контексти: Усі потоки / всі процесори бачать (або "врешті-решт погодиться") однакового порядку. Це гарантія послідовної послідовності - загального загального порядку модифікації всіх змінних. Ланцюги придбання / випуску гарантують лише сприйнятий порядок модифікації для потоків, що беруть участь у ланцюзі. Нитки поза ланцюга теоретично дозволяють бачити інший порядок.

Я не знаю, чому були введені Strongly-HB та Simply-HB. Може допомогти уточнити, як діяти навколо споживання? Сильно-НВ має приємні властивості - якщо одна нитка дотримується сильно буває перед B, вона знає, що всі потоки будуть спостерігати те саме.

Історія споживання:

Пол Е. Маккенні відповідає за те, що споживання відповідає стандартам C і C ++. Споживайте гарантії впорядкування між призначенням вказівника та пам'яттю, на яку він вказує. Він був винайдений завдяки DEC Alpha. DEC Alpha міг спекулятивно відкидати покажчик, таким чином, він також мав огорожу пам’яті, щоб запобігти цьому. DEC Alpha вже не створюється, і жоден процесор сьогодні не має такої поведінки. Споживати призначено дуже розслаблено.


1
Добре горе. Я майже шкодую, що поставив це питання. Я хочу повернутися до вирішення простих проблем C ++, таких як правила відключення ітератора, пошук аргументів, залежне від аргументів, визначених користувачем шаблонів операторів перетворення, виведення аргументу шаблону, коли пошук імені виглядає в базовому класі в члені шаблону, і коли ви може перетворити на віртуальну базу на початку побудови об'єкта.
curiousguy

Re: споживати. Ви стверджуєте, що доля замовлення споживачів пов'язана з долею DEC Alpha і не має значення поза цією аркою?
curiousguy

1
Це гарне запитання. Дивлячись на це детальніше зараз, здається, що споживати теоретично може збільшити продуктивність для слабко упорядкованих дуг, таких як ARM та PowerPC. Дайте мені ще трохи часу, щоб розглянути це.
Хамфрі Віннебаго

1
Я б сказав, що споживання існує через всі слабко впорядковані ISA, крім Alpha. У альфа-альмі єдині варіанти розслаблення та придбання (та seq-cst), а не впорядкування залежності. mo_consumeмає на меті скористатись замовленням залежності даних у реальних процесорах та формалізувати, що компілятор не може розірвати залежність даних за допомогою передбачення гілок. наприклад, int *p = load(); tmp = *p;може бути зламаний компілятором, який вводить, if(p==known_address) tmp = *known_address; else tmp=*p;якби це мало підстави очікувати, що певне значення вказівника буде загальним. Це законно для розслабленого, але не споживає.
Пітер Кордес

@PeterCordes правильно ... арки зі слабким упорядкуванням повинні випромінювати бар'єр пам'яті для придбання, але (теоретично) не для споживання. Здається, ви думаєте, що якби Альфа ніколи не існувала, ми все-таки споживали б? Крім того, ви в основному говорите, що споживання є бар'єром (або "стандартним") для компілятора.
Хамфрі Віннебаго
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.