Каковы преимущества файлов с отображением в память?

89

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

В частности, меня беспокоит следующее, в порядке важности:

  • параллелизм
  • произвольный доступ
  • производительность
  • простота использования
  • переносимость
робот
источник

Ответы:

56

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

Если ваше приложение может использовать данные «на месте» в файле с отображением памяти, они могут поступать без копирования; если вы используете системный вызов (например, pread () в Linux), то это обычно включает в себя копирование ядром данных из своих собственных буферов в пространство пользователя. Это дополнительное копирование не только требует времени, но и снижает эффективность кешей ЦП за счет доступа к этой дополнительной копии данных.

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

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


Очевидным недостатком файлов с отображением памяти является 32-разрядная ОС - у вас может легко закончиться адресное пространство.

MarkR
источник
4
В Windows, по крайней мере, вы можете отобразить несколько 32-битных представлений большого файла mmap - что может быть более эффективным, чем попытка иметь дело с очень большими файлами с помощью обычной функции CRT
Мартин Беккет
@MarkR Вы писали: «его дополнительное копирование не только требует времени, но и снижает эффективность кешей ЦП за счет доступа к этой дополнительной копии данных ». ( курсив мой). Не могли бы вы объяснить, как дополнительная буферная копия в ядре снижает эффективность кешей ЦП?
Geek
4
@Geek получает доступ к вдвое большему объему памяти = вдвое большему количеству потраченного кеша (очень приблизительно).
user253751
49

Я использовал файл с отображением памяти, чтобы реализовать функцию «автозаполнения», пока пользователь печатает. У меня более 1 миллиона номеров деталей, хранящихся в одном индексном файле. Файл имеет типичную информацию заголовка, но основная часть файла представляет собой гигантский массив записей фиксированного размера, отсортированных по ключевому полю.

Во время выполнения файл отображается в память, преобразуется в массив Cстиля struct, и мы выполняем двоичный поиск, чтобы найти совпадающие номера деталей в соответствии с типом пользователя. На самом деле с диска считываются только несколько страниц памяти файла - какие бы страницы ни были обнаружены во время двоичного поиска.

  • Параллелизм - у меня была проблема реализации, когда он иногда отображал файл в памяти несколько раз в одном и том же пространстве процесса. Насколько я помню, это было проблемой, потому что иногда система не могла найти достаточно большой свободный блок виртуальной памяти для сопоставления файла. Решение заключалось в том, чтобы сопоставить файл только один раз и обработать все обращения к нему. Оглядываясь назад, было бы круто использовать полноценную службу Windows.
  • Произвольный доступ - бинарный поиск, безусловно, является произвольным и молниеносным.
  • Производительность - поиск выполняется очень быстро. По мере того, как пользователи вводят во всплывающем окне список совпадающих номеров деталей продуктов, список сокращается по мере того, как они продолжают вводить текст. При наборе текста заметных задержек нет.
Брайан Энсинк
источник
1
Разве двоичный поиск не будет медленным, поскольку страницы читаются при каждой попытке? Или операционная система достаточно умен, чтобы справиться с этим эффективным способом?
jjxtra 02
1
Я полагаю, что использование ввода-вывода с отображением памяти является своего рода расточительным для двоичного поиска, поскольку поиск будет обращаться только к нескольким одиночным ключам в относительно удаленных местах памяти, но ОС будет загружать 4 КБ страниц для каждого такого запроса. Но опять же, файл с частями не сильно меняется, поэтому кеш помогает скрыть это. Но, строго говоря, я считаю, что традиционное чтение / поиск здесь было бы лучше. Наконец, 1 миллион в наши дни - это немного. Почему бы просто не хранить все это в ОЗУ?
the swine
5
@ the swine and PsychoDad, мой первоначальный ответ был из 2008 года, а фактическая реализация этой функции автозаполнения с отображением памяти была примерно в 2004-2005 годах. Использование 800–1000 МБ физической памяти для загрузки всего файла не было хорошим решением для нашей пользовательской базы. Решение с отображением памяти было очень быстрым и эффективным. Это было круто, и я вспоминаю его с любовью с тех пор, как я был младшим разработчиком. :)
Брайан Энсинк
@BrianEnsink: хорошо, в этом есть смысл. Я не ожидал, что каждая запись будет целых 1 КБ. тогда, конечно, постраничный подход становится более эффективным. приятно :)
свинья
22

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

Вместо того, чтобы искать, писать и читать в файле, вы отображаете его в память и просто обращаетесь к битам там, где вы их ожидаете.

Это может быть очень удобно и в зависимости от интерфейса виртуальной памяти может повысить производительность. Повышение производительности может произойти, потому что операционная система теперь может управлять этим бывшим «файловым вводом-выводом» вместе со всем вашим другим программным доступом к памяти и может (теоретически) использовать алгоритмы разбиения на страницы и т. Д., Которые она уже использует для поддержки виртуальная память для остальной части вашей программы. Однако это зависит от качества вашей базовой системы виртуальной памяти. Я слышал анекдоты, в которых говорится, что системы виртуальной памяти Solaris и * BSD могут показать лучшие улучшения производительности, чем система виртуальных машин Linux, но у меня нет эмпирических данных, подтверждающих это. YMMV.

Параллелизм становится очевидным, если учесть возможность того, что несколько процессов используют один и тот же «файл» через отображаемую память. В модели чтения / записи, если два процесса записывают данные в одну и ту же область файла, вы можете быть уверены, что данные одного процесса поступят в файл, перезаписав данные другого процесса. Вы получите одно или другое, но не какое-то странное смешение. Я должен признать, что не уверен, соответствует ли такое поведение каким-либо стандартам, но на это можно в значительной степени положиться. (На самом деле это хороший дополнительный вопрос!)

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

Вот пример. Скажем, у меня есть два процесса, которые записывают 8 байтов со смещением 1024. Процесс 1 записывает «11111111», а процесс 2 записывает «22222222». Если они используют файловый ввод-вывод, то вы можете представить себе, что глубоко внутри O / S есть буфер, полный единиц, и буфер, полный двоек, оба направляются в одно и то же место на диске. Один из них доберется туда первым, а другой - вторым. В этом случае выигрывает второй. Однако , если я использую файловый подход с отображением памяти, процесс 1 перейдет в хранилище памяти размером 4 байта, за которым следует другое хранилище памяти из 4 байтов (предположим, что это не максимальный размер хранилища памяти). Процесс 2 будет делать то же самое. В зависимости от того, когда выполняются процессы, вы можете ожидать увидеть любое из следующего:

11111111
22222222
11112222
22221111

Решением является использование явного взаимного исключения, что, вероятно, в любом случае является хорошей идеей. В любом случае, вы вроде бы полагались на то, что операционная система сделает «правильные вещи» при чтении / записи файлового ввода-вывода.

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

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

болото
источник
1

Параллелизм будет проблемой. Произвольный доступ проще. Производительность от хорошей до отличной. Удобство использования. Не так хорошо. Портативность - не ахти.

Я использовал их в системе Sun давным-давно, и это мои мысли.

Пол Натан
источник