Возвращается ли память из mmapping / dev / shm с обратной записью (WB) или без кэшируемого объединения с записью (W C) в Linux / x86? - PullRequest
3 голосов
/ 21 апреля 2020

У меня есть два процесса C ++, которые обмениваются данными с отображением в памяти Single-Producer Single-Consumer (SPS C) двойной буфер . Процессы будут работать только в Linux / Intel x86-64. Семантика заключается в том, что производитель заполняет передний буфер, а затем меняет указатели и обновляет счетчик, давая потребителю знать, что он может memcpy() задний буфер. Все совместно используемые состояния хранятся в блоке заголовка в начале отображаемой области.

int _fd;
volatile char *_mappedBuffer;

...

_fd = shm_open("/dev/shm/ipc_buffer", O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
...
_mappedBuffer = static_cast<char *>(mmap(nullptr, _shmFileSizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE | MAP_POPULATE, _fd, 0));

Производителю необходим барьер StoreStore, чтобы гарантировать, что своп виден до приращения счетчика, что должно быть неявным для x86, с памятью обратной записи (WB) :

void produce() {
    ...

    // swap pointers
    char *tmp = _frontBuffer;
    _frontBuffer= _backBuffer;
    _backBuffer= tmp;

    ...

    // SFENCE needed here? Yes if uncacheable WC, NO if WB due to x86 ordering guarantees?
    asm volatile ("sfence" ::: "memory");

    _flipCounter++;
}

Потребителю необходим барьер LoadLoad, если (W C), чтобы он загружал счетчик переворачивания до того, как новый указатель заднего буфера. Если память (WB), то мы знаем, что процессор не может переупорядочить нагрузки:

bool consume(uint64_t &localFlipVer, char *dst) {
    if (localFlipVer < _flipCounter) {
        // LFENCE needed here? Yes if uncacheable WC, NO if WB due to x86 ordering guarantees?
        asm volatile ("lfence" ::: "memory");

        std::memcpy(dst, _backBuffer, _bufferSize);
        localFlipVer++;
        return true;
    }

    return false;
}

Мой вопрос и мои предположения :

Is область отображения в памяти, возвращаемая mmapping /dev/shm Write-Back или Non-cacheable Write-Combining? В последнем случае хранилища и загрузки заказаны слабо и не следуют традиционным гарантиям заказа x86 (без повторного заказа StoreStore или LoadLoad) согласно

https://hadibrais.wordpress.com/2019/02/26/the-significance-of-the-x86-sfence-instruction/

https://preshing.com/20120913/acquire-and-release-semantics/#IDComment721195741

https://software.intel.com/en-us/forums/software-tuning-performance-optimization-platform-monitoring/topic/596002

и, следовательно, мне придется использовать SFENCE и LFENCE, в то время как обычно (с WB), я мог бы обойтись только с барьером компилятора asm volatile ("" ::: "memory");

1 Ответ

5 голосов
/ 21 апреля 2020

/dev/shm/ - это просто точка монтирования tmpfs, например /tmp.

Память, которую вы mmap, в файлах обычно кешируется в WB, как и MAP_ANONYMOUS. Он следует обычным правилам упорядочения памяти x86 (программный порядок + буфер хранилища с пересылкой в ​​хранилище), поэтому вам не нужны SFENCE или LFENCE, а только блокировка переупорядочения во время компиляции для упорядочивания acq_rel. Или для seq_cst, MFENCE или заблокированной операции, например, использование xchg для хранения.

Вы можете использовать функции C11 <stdatomic.h> для указателей на SHM, для типов, которые lock_free. (Обычно любой размер степени 2 вплоть до ширины указателя.)

Объекты без блокировки используют таблицу блокировок ha sh в адресном пространстве процесса, выполняющего операцию, поэтому разделяйте процессы не будут уважать блокировки друг друга. 16-байтовые объекты могут по-прежнему использовать lock cmpxchg16b, который является безадресным и работает во всех процессах, даже если GCC7 и более поздние версии сообщают, что он не является свободным от блокировки по причинам , даже если вы компилируете с -mcx16.


Я не думаю, что в основном ядре Linux есть способ для пользовательского пространства выделить память любого типа, кроме WB. (За исключением X-сервера или клиентов прямого рендеринга, отображающих видеопамять RAM; я не имею в виду способ сопоставления обычных страниц DRAM с другим типом памяти PAT.) См. Также При использовании политики сквозного кэширования для страниц

Любой тип, кроме WB, может быть потенциальным падением производительности для обычного кода, который не пытается группировать хранилища в одном широком SIMD-хранилище. Например, если бы у вас была структура данных в SHM, защищенная общим мьютексом, было бы плохо, если бы обычный доступ внутри критической секции был не кэшируемым. Особенно в неконтролируемом случае, когда один и тот же поток постоянно захватывает одну и ту же блокировку и читает / записывает одни и те же данные.

Так что есть очень веская причина, почему это всегда WB.

...