2014-10-20 4 views
2

Я открываю файл, используя библиотеку форматированных карт. Можно ли использовать «fetch_add» (значение считывается в определенной позиции, затем добавляется другим и записывается обратно в ту же самую позицию атомарно) в этом отображаемом файле?C++: Fetch_add в файле с отображением памяти

Если несколько потоков записи в него параллельно могут возникнуть проблемы без атомарностью участие

Файл находится в двоичном формате и содержит Интс или двойники (зависит от конкретного файла).

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

Есть ли какие-либо улучшения, чтобы несколько потоков могли записывать в сопоставленный файл с высокой производительностью?

Спасибо. Laz

ответ

1

Есть ли несколько процессов, сопоставляющих этот файл или несколько потоков?

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

Если это только несколько потоков, то вы можете атомно обновлять память так же, как вы делали бы это для любого другого слова памяти, с предостережением, которое вы не можете использовать std::atomic (потому что, очевидно, байты соответствуют раздел в файле, а не std::atomic). Итак, вы должны прибегнуть к использованию поддержки вашей конкретной платформы для атомно-модифицирующей памяти, а именно: lock xadd на x86 через, например, InterlockedIncrement на Win32 (или __sync_fetch_and_add с g ++). Будьте осторожны, чтобы обеспечить семантику упорядочения памяти (и возвращаемое значение!), Как вы ожидаете.

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

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

+0

Существует только несколько потоков, обращающихся к отображению. Итак, std :: atomic - это просто для одиночных значений, и нет возможности сделать блок памяти атомарным, например массив. За исключением того, что каждый элемент представляет собой атомное одиночное значение? Не учитывая специфику платформы. Спасибо за быстрый ответ! – Lazarus535

+0

виртуальный +1 для __sync_fetch_and_add! Слишком низкая репутация еще :-( – Lazarus535

+0

@ Lazarus535: 'std :: atomic' - это шаблон, поэтому теоретически вы можете иметь атомную переменную любого типа, включая массив или большую структуру. Однако [только определенные специализации блокируются -free] (http://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free) (в идеале все те аппаратные средства, которые поддерживаются с учетом эффективной реализации стандартной библиотеки), и атомарность достигается посредством нормального mutex (внутренний для реализации) для всех других типов. Обратите также внимание на то, что массив атомных переменных не является тем же самым, что и атомная переменная, содержащая массив :-) – Cameron

1

Поскольку файл с отображением памяти ведет себя как обычная память [1], он будет работать так же, как и на любой другой части памяти.

компилируется (при помощи clang++3.6 -Wall -Wextra), но технически не определен (поскольку std::atomic<T> не гарантируется тот же тип или имеют те же правила выравнивания как T):

std::atomic<uint64_t> *p; 
    p = reinterpret_cast<std::atomic<uint64_t>*>(&dest[index]); 
    p->fetch_add(value); 

Это должно быть хорошо в г ++ и лязг ++:

dest[index] = __sync_fetch_and_add(&dest[index], value); 

(Оба генерируют почти идентичный код на ассемблере, - последний использует xaddq [который возвращает исходное значение], где прежние использования addq - usign __sync_add_and_fetch будет делать то же addq, я ожидаю)

[1], потому что это обычная память - механизм отображения в Linux точно такой же механизм, который обрабатывает обычную память, например, обмен данными в /, когда ваши там недостаточно памяти, или если вы используете код/​​данные в только что запущенном приложении. Хотя у меня нет доступа к исходному коду Windows, я считаю, что это тоже так. Другие ОС вполне могут реализовать его по-разному, но нет оснований полагать, что это предотвратит работу атома.

+0

Я действительно попытаюсь направить указатель на std :: atomic, чтобы увидеть, работает ли он. Кажется очень противным мне :-) Спасибо. – Lazarus535

+2

Просто имейте в виду, что нет гарантии, что это не сломается в какой-то момент в будущем - или если вы скомпилируете другой процессор и т. Д. –

Смежные вопросы