Наименьшее ограничение порядка памяти для кольцевого буфера одного производителя?

У меня есть RingBuffer, который обслуживает одного потребителя и одного производителя и использует два целых числа для обнаружения новых данных:

_lastReadIndex
_lastWrittenIndex

поэтому в кольцевом буфере есть непрочитанные данные, когда эти два значения не равны.

Продюсер приращений (и модули с размером кольцевого буфера, чтобы обернуть) _lastWrittenIndex когда элемент добавлен в кольцевой буфер.

Потребительские спины, чтение оба значения, проверка на новые данные, и когда есть, это будет приращение (и модуль) _lastReadIndex,

Три выделенных термина подчеркивают требования в отношении многопоточности и барьеров памяти.

Насколько я могу уменьшить порядок памяти для этой конструкции, учитывая модель памяти Intel? Я считаю, что модель памяти Intel позволяет переупорядочивать нагрузки с более ранними магазинами по разным адресам?

РЕДАКТИРОВАТЬ с использованием атомарной библиотеки C ++ 11 std::memory_order_xxxx так далее

5

Решение

Несколько вещей, которые вы должны сделать прежде всего:

Модуль чтения и письма, но держать _lastReadIndex а также _lastWrittenIndex неповрежденным, чтобы знать, сколько данных у вас есть, сколько потеряно или может заблокировать устройство записи, если оно переполняет читателя после полного цикла.

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

Теперь к вашему вопросу:

Если вы пытаетесь быть переносимым, порядок памяти, который вам требуется в вашем коде, не должен учитывать архитектуру. Стандартные атомарные функции могут позаботиться об этом.
Вам нужно только убедиться, что данные доступны в буфере, прежде чем увеличивать индекс записи, что означает семантику освобождения приращения.
Вы также должны убедиться, что записывающее устройство записывает данные в память и не оптимизировано, чтобы оставаться только в регистрах.

newIndex = _lastWrittenIndex+1;
buffer[newIndex % bufSize] = newData;
atomic_store( &_lastWrittenIndex, newIndex, memory_order_release );

На x86 / 64 это будет так же, как:

newIndex = _lastWrittenIndex+1;
buffer[newIndex % bufSize] = newData;
// release semantics means reorder barrier before action:
barrier(); // translates to `asm volatile("":::"memory");`
*(volatile int*)_lastWrittenIndex = newIndex;

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

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]