Чтение / запись 64 бит на двух потоках без мьютекса / блокировки / атомарности - PullRequest
0 голосов
/ 01 февраля 2020

У меня есть std::array 64-битных структур, где каждая структура выглядит следующим образом:

MyStruct
{
    float _a{0};
    float _b{0};
};  // Assume packed

Один поток (ядро ЦП) будет записывать 64-битный объект, а второй поток (другое ядро) ) прочитает его.

Я использую архитектуру Intel x86, и я знаю, что 64-разрядные записи гарантированно будут атомами c из Руководств Intel для разработчиков.

Однако меня беспокоит, что второй поток может кэшировать значение в регистре и не обнаруживать, когда значение изменилось.

  • Гарантирует ли протокол MESIF, что второй поток видит записи?
  • Нужно ли ключевое слово volatile, чтобы сообщить компилятору, что другой поток может изменять память?
  • Нужна ли атомика?

Поток, записывающий значения, чрезвычайно чувствителен к производительности, и я хотел бы избежать барьеров памяти, мьютексов и c, если смогу.

Ответы [ 4 ]

1 голос
/ 01 февраля 2020

Независимо от того, будет ли volatile устаревшим в следующей версии C ++ - volatile никогда не был предназначен или предназначен для использования в многопоточности ! Это в отличие от Java, где volatile означает нечто совершенно иное (Java volatile семантика гораздо ближе к таковой в атомарности C ++).

Было бы хорошо получить больше информации о фактической проблема, т. е. еще немного контекста о том, чего вы на самом деле пытаетесь достичь.

Исходя из вашего описания, у вас есть только два потока - одно чтение и одно письмо - я бы предложил использовать один производитель-один Потребительская очередь. Такая очередь может быть реализована только с двумя счетчиками атомов c для индексов головы / хвоста; сами значения не должны быть атомами c и могут быть любого типа (включая нетривиально копируемые).

Но чтобы понять, будет ли это правильным решением, мне нужно больше информации : Должны ли предметы потребляться FIFO или LIFO? Как насчет массива? Насколько большой это? Может ли он переполниться / опуститься (то есть потоки пытаются записывать / читать записи, но массив заполнен / пуст)? Как обрабатывать полный / пустой массив?

0 голосов
/ 02 февраля 2020
  • MESIF - не влияет на регистры.
  • Volatile - раньше использовался для этого, но в более новых компиляторах он только для чтения из аппаратных регистров для принудительного чтения, поэтому он не будет работа.
  • атомика - как только у вас есть два потока, которые взаимодействуют вместе, вам понадобятся операции атома c для чтения и записи.

    std :: atomi c whatEver ;

Если вам повезет, вы получите

 bool is_lock_free = whatEver.is_lock_free();

и еще более удачливым, если

 bool is_lock_free = whatEver.is_always_lock_free();

вам придется сделать

 auto whatEver = arrayOfWhatEver[x]; // atomic
 auto a = whatEver._a;

для использования операции atomi 1025 *, а не только для чтения отдельных членов MyStruct.

0 голосов
/ 01 февраля 2020

Как разработчик C ++, вы должны работать на низком уровне с небольшим количеством соли. Посмотрите этот интересный вопрос: ' Понимание std :: hardware_destructive_interference_size и std :: hardware_constructive_interference_size '

Истинное совместное использование происходит между строками кэша, из того, что мы видим, следует изменить приведенную выше структуру следующим образом:

struct MyStruct
{
    alignas ( hardware_constructive_interference_size ) atomic < float > _a;
    alignas ( hardware_constructive_interference_size ) atomic < float > _b;
}; 

Для одновременного доступа к переменным всегда требуется использование std :: atomi c. Если ваша цель готова выполнить запись по порядку, это не имеет значения для вас в C ++. Многое происходит под капотом, и, наконец, volatile не работает, оно заменено std::atomic и устарело.

0 голосов
/ 01 февраля 2020

Будет ли протокол MESIF гарантировать, что второй поток видит записи?

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

Нужно ли ключевое слово volatile, чтобы сообщить компилятору, что другой поток может изменять память?

volatile говорит компилятору не оптимизировать переменные away , вообще не оптимизировать его.

Нужна ли атомика?

Зависит от того, что вы не планируете использовать мьютексы, вы не планируете использовать что-либо, относящееся к удаленному параллелизму, за исключением атомики, которая больше предназначена для записи между потоками.

Я предлагаю использовать std :: mutex в сочетании с std::lock_guard или std::scoped_lock.

Я знаю, что ваш заголовок говорит, что вы этого не хотите, но это действительно единственный способ гарантировать один и тот же порядок чтения и записи каждый раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...