powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / Почему в примере для compare_exchange_strong() используют memory_order_acquire?
19 сообщений из 19, страница 1 из 1
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38258099
Почему в примере для std::atomic<bool> flag; в функции flag.compare_exchange_strong() используют memory_order_acquire, а не memory_order_acq_rel или memory_order_seq_cst ?

Они ведут речь об упорядочивании команд процессором/компилятором в пределах одного потока благодаря барьеру памяти, но не об упорядочивании команд между потоками/ядрами.
Допустим, что будет если два потока на двух разных ядрах одновременно выполнят инструкцию, при flag=0?
if (flag.compare_exchange_strong(expected, 1, memory_order_acquire)) { ... }
Тогда:
- 1й поток благодаря memory_order_acquire прочитает значение flag==0 и запишет flag=1
- 2й поток благодаря memory_order_acquire снова прочитает значение flag==0 (т.к. 1й поток записал flag=1 без release), и тоже запишет flag=1
Причем это будет возникать крайне редко.

Собственно вопрос, почему не memory_order_acq_rel и в каком случае понадобился бы memory_order_seq_cst?
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259217
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_acquireДопустим, что будет если два потока на двух разных ядрах одновременно выполнят инструкцию, при flag=0?
if (flag.compare_exchange_strong(expected, 1, memory_order_acquire)) { ... }
Тогда:
- 1й поток благодаря memory_order_acquire прочитает значение flag==0 и запишет flag=1
- 2й поток благодаря memory_order_acquire снова прочитает значение flag==0 (т.к. 1й поток записал flag=1 без release), и тоже запишет flag=1
Причем это будет возникать крайне редко.

Не будет этого никогда. compare_exchange_strong - это атомарная операция, которая не может быть успешной одновременно в двух потоках.
А барьер относится не к самому флагу, а к командам которые предшествуют/следуют за его установкой. Если не делать барьер то возможна ситуация когда команды которые должны быть до установки флага на самом деле выполнятся уже после и наоборот, при сбросе флага.

Пары барьеров acquire и release для реализации мьютекса достаточно, т.к. memory_order_seq_cst избыточно мощный (например нужен для случая когда одновременно несколько потоков выполняют действия при изменении флага, и каждый из них должен видеть действия всех потоков одинаково, как пример - очередь с несколькими потребителями: если один поток извлек сообщение, то остальные не должны его повторно извлечь).
В случае же мьютекса одновременно действуют только два потока (остальные ждут), причем один из них (тот кто отпускает мьютекс) только что совершил действие, а второй (тот кто захватывает) только собирается совершить, и первому не надо читать результат второго, а второй точно знает что первый ничего нового не сделает. Это и есть семантика acquire-release.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259337
Anatoly Moskovskymemory_order_acquireДопустим, что будет если два потока на двух разных ядрах одновременно выполнят инструкцию, при flag=0?
if (flag.compare_exchange_strong(expected, 1, memory_order_acquire)) { ... }
Тогда:
- 1й поток благодаря memory_order_acquire прочитает значение flag==0 и запишет flag=1
- 2й поток благодаря memory_order_acquire снова прочитает значение flag==0 (т.к. 1й поток записал flag=1 без release), и тоже запишет flag=1
Причем это будет возникать крайне редко.

Не будет этого никогда. compare_exchange_strong - это атомарная операция, которая не может быть успешной одновременно в двух потоках.
А барьер относится не к самому флагу, а к командам которые предшествуют/следуют за его установкой. Если не делать барьер то возможна ситуация когда команды которые должны быть до установки флага на самом деле выполнятся уже после и наоборот, при сбросе флага.
Т.е. если коротко, то так?
memory_order_acquire - гарантирует, что все предшествующие изменению атомарной переменной команды выполнены
memory_order_release - гарантирует, что все следующие за изменением атомарной переменной команды выполнятся строго после

Или наоборот? Ведь нам не так страшно, что на самом деле команды могут выполниться после после, как до установки флага, когда мьюеткс ещё не захватили, а изменения им защищаемые уже прошли.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259369
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_acquireТ.е. если коротко, то так?
memory_order_acquire - гарантирует, что все предшествующие изменению атомарной переменной команды выполнены
memory_order_release - гарантирует, что все следующие за изменением атомарной переменной команды выполнятся строго после

compare_exchange выглядит как запись, но на самом деле там внутри две операции: чтение флага (для проверки старого содержимого) и потом запись.
Когда мы захватываем (acquire) мьютекс, то барьер относится к чтению флага и служит для предотвращения выполнения последующих команд до собственно чтения флага и проверки что мьютекс свободен.
Когда мы освобождаем (release) мьютекс, то барьер относится к записи флага и служит для предотвращения выполнения предыдущих ему команд после собственно установки флага.

Т.е. гарантируется такой порядок:
* проверка что flag == 0
* acquire
* какие-то команды которые не выйдут за пределы acquire-release
* release
* flag = 0
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259381
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, хоть это и видно из предыдущего текста но хочу явно ответить на этот вопрос:
memory_order_acquirememory_order_acquire - гарантирует, что все предшествующие изменению атомарной переменной команды выполнены
memory_order_release - гарантирует, что все следующие за изменением атомарной переменной команды выполнятся строго после
Или наоборот?

Наоборот.
acquire не пускает последующие команды до себя
release не пускает предыдущие команды после себя

ЗЫ.Когда речь про барьеры, под порядком команд имеются в виду не собственно порядок выполнения команд, а порядок в котором эффекты от выполнения команд видны остальным потокам.
Потому что например одна команда может сохранить во внешнюю память, а другая выполненная после нее - в кеш. Для других потоков порядок будет обратный, поскольку запись в кеш будет позже увидена.
Барьеры решают именно эту проблему - приводят очередность эффектов к очередности команд в коде.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259389
Anatoly MoskovskyДа, хоть это и видно из предыдущего текста но хочу явно ответить на этот вопрос:
memory_order_acquirememory_order_acquire - гарантирует, что все предшествующие изменению атомарной переменной команды выполнены
memory_order_release - гарантирует, что все следующие за изменением атомарной переменной команды выполнятся строго после
Или наоборот?

Наоборот.
acquire не пускает последующие команды до себя
release не пускает предыдущие команды после себя

ЗЫ.Когда речь про барьеры, под порядком команд имеются в виду не собственно порядок выполнения команд, а порядок в котором эффекты от выполнения команд видны остальным потокам.
Потому что например одна команда может сохранить во внешнюю память, а другая выполненная после нее - в кеш. Для других потоков порядок будет обратный, поскольку запись в кеш будет позже увидена.
Барьеры решают именно эту проблему - приводят очередность эффектов к очередности команд в коде.
Ясно, спасибо. Значит memory_order_acquire, memory_order_release, memory_order_acq_rel решают проблему переупорядочивания.

Но тогда не совсем понимаю, чем memory_order_seq_cst сильнее, чем memory_order_acq_rel? Ведь memory_order_acq_rel уже гарантирует, что ни в одну, ни в другую сторону видимые действия команд не перескочат.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259420
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_seq_cstНо тогда не совсем понимаю, чем memory_order_seq_cst сильнее, чем memory_order_acq_rel?
Разница между ними следующая.
memory_order_acq_rel задает порядок команд в том потоке где он применен. Т.е. другие потоки увидят эффекты команд до барьера и после барьера именно в таком порядке.
Но если например таких потоков будет несколько, то остальные потоки могут видеть порядок самих барьеров разным.
memory_order_seq_cst гарантирует что порядок барьеров будет одним и тем же с точки зрения каждого потока. Технически это реализуется например тем что барьер сопровождается командой блокировки внешней памяти, что гарантирует сериализацию таких команд на всех процессорах.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259434
Anatoly Moskovskymemory_order_seq_cstНо тогда не совсем понимаю, чем memory_order_seq_cst сильнее, чем memory_order_acq_rel?
Разница между ними следующая.
memory_order_acq_rel задает порядок команд в том потоке где он применен. Т.е. другие потоки увидят эффекты команд до барьера и после барьера именно в таком порядке.
Но если например таких потоков будет несколько, то остальные потоки могут видеть порядок самих барьеров разным .
memory_order_seq_cst гарантирует что порядок барьеров будет одним и тем же с точки зрения каждого потока. Технически это реализуется например тем что барьер сопровождается командой блокировки внешней памяти, что гарантирует сериализацию таких команд на всех процессорах.
В смысле без memory_order_seq_cst остальные потоки могут видеть, что сначала поток 2 изменил атомарную переменную, а затем поток 1, хотя реально было наоборот, сначала 1, потом 2?

А memory_order_seq_cst именно через блокировку внешней памяти реализуется или использует и какие-то команды синхронизации кэшей, или все же кэши сами решают эти проблемы используя протоколы поддержки когерентности кешей AMD MOESI / Intel MESIF , т.е. ни на каких процессорах не может быть двух одинаковых кэш-линий модифицированных по разному разными ядрами, даже если каждая из этих кэш линий в отдельном L1?
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259450
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_seq_cstВ смысле без memory_order_seq_cst остальные потоки могут видеть, что сначала поток 2 изменил атомарную переменную, а затем поток 1, хотя реально было наоборот, сначала 1, потом 2?
Речь про две разные атомарные переменные.

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

memory_order_seq_cstили все же кэши сами решают эти проблемы используя протоколы поддержки когерентности кешей AMD MOESI / Intel MESIF, т.е. ни на каких процессорах не может быть двух одинаковых кэш-линий модифицированных по разному разными ядрами, даже если каждая из этих кэш линий в отдельном L1?
См первый ответ. Речь не про одну переменную а разные. Поэтому когерентность не поможет.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259466
Anatoly Moskovskymemory_order_seq_cstВ смысле без memory_order_seq_cst остальные потоки могут видеть, что сначала поток 2 изменил атомарную переменную, а затем поток 1, хотя реально было наоборот, сначала 1, потом 2?
Речь про две разные атомарные переменные.

memory_order_seq_cstА memory_order_seq_cst именно через блокировку внешней памяти реализуется или использует и какие-то команды синхронизации кэшей
Я не знаю как еще кроме блокировки шины можно это сделать. Но наверно можно и по другому (но что-то внешнее по отношению к процессору по идее заблокируется :)).
Ну это понятно :) Вопрос, заблокируется только ОЗУ или и кэш тоже?
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259473
И отдельная ситуация, все таки на каких-то процессорах может быть две одинаковых кэш-линий модифицированных по разному разными ядрами, если допустим каждая из этих кэш линий в отдельном L1? И если может то как решается?
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38259486
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_seq_cstВопрос, заблокируется только ОЗУ или и кэш тоже?
memory_order_seq_cstИ отдельная ситуация, все таки на каких-то процессорах может быть две одинаковых кэш-линий модифицированных по разному разными ядрами, если допустим каждая из этих кэш линий в отдельном L1? И если может то как решается?
Не знаю
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38260959
Ок, будем считать, что memory_order_seq_cst помимо прочего, скидывает данные из L1 в L3 и RAM, а конфликты кэшей решаются исключительно на уровне протоколов когерентности кешей.

И уж для полноты картины std::memory_order_consume - говорят что "prior writes to data-dependent memory locations", а в memory_order_acquire - "prior writes made to other memory locations". Непонятно только про какие other/data-dependent memory locations идет речь если мы работаем с одной и той же атомарной переменной и в чем их отличие?
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38261066
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_seq_cstОк, будем считать, что memory_order_seq_cst помимо прочего, скидывает данные из L1 в L3 и RAM, а конфликты кэшей решаются исключительно на уровне протоколов когерентности кешей.
Конечно скидывает, иначе бы он не работал, как барьер release :)

memory_order_seq_cstИ уж для полноты картины std::memory_order_consume - говорят что "prior writes to data-dependent memory locations", а в memory_order_acquire - "prior writes made to other memory locations". Непонятно только про какие other/data-dependent memory locations идет речь если мы работаем с одной и той же атомарной переменной и в чем их отличие?
data-dependent - например если мы записываем сообщение в структуре, а другому потоку передаем указатель на нее в атомарной переменной.
memory_order_consume указанный при чтении указателя не позволяет прочесть данные по предыдущему значению указателя, а заставляют сначала прочесть новый указатель, а потом и данные по нему.
Однако другие операции которые не связаны с данным указателем, могут быть переупорядочены.

memory_order_acquire запрещает переупорядочивание любых последующих за ним операций. Т.е. более строгий.
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38287626
Anatoly Moskovskymemory_order_seq_cstОк, будем считать, что memory_order_seq_cst помимо прочего, скидывает данные из L1 в L3 и RAM, а конфликты кэшей решаются исключительно на уровне протоколов когерентности кешей.
Конечно скидывает, иначе бы он не работал, как барьер release :)
В смысле, что release не скидывает данные из L1 в L2, а только занимается пере-упорядочиванием?

Anatoly Moskovskymemory_order_seq_cstВ смысле без memory_order_seq_cst остальные потоки могут видеть, что сначала поток 2 изменил атомарную переменную, а затем поток 1, хотя реально было наоборот, сначала 1, потом 2?
Речь про две разные атомарные переменные.
А можете привести какой-то простой кейс применения memory_order_seq_cst, а то в той же статье из хабра , если счетчик сделать атомарным, то речь как раз будет о двух атомарных переменных, но ведь memory_order_seq_cst не понадобиться же :)
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38287657
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_seq_cstВ смысле, что release не скидывает данные из L1 в L2, а только занимается пере-упорядочиванием?
Вы зря на такой уровень опускаетесь.
Когда вы применяете семантики упорядочивания то вам вообще не зачем знать про кэши.
Именно для того чтобы не приходилось об этом заботиться и придумали эти абстракции.
А если отвечать на вопрос - release не занимается пере-упорядочиванием, он наоборот сохраняет порядок
Между release в одном потоке и acquire в другом будут произведены все необходимые "скидывания из кэшей"
memory_order_seq_cstА можете привести какой-то простой кейс применения memory_order_seq_cs
Мне пример лень придумывать, см. тут в конце http://en.cppreference.com/w/cpp/atomic/memory_order
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38315176
А std::memory_order_consume в принципе можно применять не только для указателей, но и для индексов и вообще любых любых переменных/объектов зависящих от атомика с барьером std::memory_order_consume?

И так, используя не указатель, а индекс, тоже можно?
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
#include <iostream>
 
#include <thread>
#include <atomic>
#include <cassert>
#include <string>
#include <vector>
 
std::vector<std::string> array_of_strings;
std::atomic<size_t> index;
int data;
 
void producer()
{
	array_of_strings.emplace_back(std::string("Hello"));
    data = 42;
    index.store(array_of_strings.size()-1, std::memory_order_release);
}
 
void consumer()
{
    size_t p2;
    while (!(p2 = index.load(std::memory_order_consume)))
        ;
    assert(array_of_strings[p2] == "Hello"); // never fires
    assert(data == 42); // may or may not fire
}
 
int main()
{
	index = 0;

    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join(); t2.join();

	int val_cin;
	std::cin >> val_cin;
}




P.S. Мда, в MSVS 2012 до сих пор не реализовали ATOMIC_VAR_INIT(), и в один из примеров в вообще валиться с ассертом.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
#include <thread>
#include <atomic>
#include <cassert>
#include <vector>
 
std::vector<int> data;
std::atomic<int> flag = ATOMIC_VAR_INIT(0);
 
void thread_1()
{
    data.push_back(42);
    flag.store(1, std::memory_order_release);
}
 
void thread_2()
{
    int expected=1;
    while (!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
        expected = 1;
    }
}
 
void thread_3()
{
    while (flag.load(std::memory_order_acquire) < 2)
        ;
    assert(data.at(0) == 42); // will never fire
}
 
int main()
{
    std::thread a(thread_1);
    std::thread b(thread_2);
    std::thread c(thread_3);
    a.join(); b.join(); c.join();
}

...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38315281
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
memory_order_consume не только дИ так, используя не указатель, а индекс, тоже можно?

Индекс и указатель - это одно и то же, как лингвистически, так и с точки зрения стандарта С/С++ :)

Код: plaintext
1.
a[i] === *(a + i)
...
Рейтинг: 0 / 0
Почему в примере для compare_exchange_strong() используют memory_order_acquire?
    #38315288
Anatoly Moskovskymemory_order_consume не только дИ так, используя не указатель, а индекс, тоже можно?

Индекс и указатель - это одно и то же, как лингвистически, так и с точки зрения стандарта С/С++ :)

Код: plaintext
1.
a[i] === *(a + i)


Я так понял тут хоть float используй, тоже барьер сработает :)
Все зависимые переменные от атомика не смогут выполниться раньше загрузки(load) из атомика.
...
Рейтинг: 0 / 0
19 сообщений из 19, страница 1 из 1
Форумы / C++ [игнор отключен] [закрыт для гостей] / Почему в примере для compare_exchange_strong() используют memory_order_acquire?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]