mmap () vs read ()

Я пишу большой редактор тегов ID3 ​​в C. Теги ID3 обычно находятся в начале файла, закодированного в mp3, хотя tags старше (версия 1) находятся в конце. Приложение предназначено для приема списка каталогов и идентификаторов фреймов из командной строки, а затем рекурсирует структуру каталогов, обновляющую все найденные tags ID3. Пользователь может дополнительно удалить все старые (версии 1) tags. Другой вариант – просто отображать текущие tags, не выполняя обновление. Каталог может содержать 2 файла или 2 миллиона. Если пользователь хочет обновить файлы, я планировал загрузить весь файл в память, выполнить обновления, а затем сохранить его (файл также можно переименовать). Однако, если пользователь только предназначен для печати текущих тегов ID3, то загрузка всего файла кажется чрезмерной. Ведь файл может быть 200 Мб.

Я прочитал эту тему, которая была проницательной – mmap () против блоков чтения

Поэтому мой вопрос: какой наиболее эффективный способ сделать это – read (), mmap () или некоторая комбинация? Идеи дизайна приветствуются.

ТИА,

Эндрю

Edit: Понятно, что mmap по сути делегирует загрузку файла в память, в подсистему виртуальной памяти. Мне кажется, что VMM будет сильно оптимизирован для большинства систем, поскольку это важно для производительности системы.

Это действительно зависит от того, что вы пытаетесь сделать. Если все, что вам нужно сделать, это перейти к известному смещению и зачитать небольшой тег, read() может быть быстрее ( mmap() должен выполнить довольно сложный внутренний учет). Однако, если вы планируете копировать все 200 мб MP3, или сканировать его для некоторого тега, который может появиться с неизвестным смещением, тогда mmap() , скорее всего, будет более быстрым.

Например, если вам нужно переместить весь файл на несколько сотен байт, чтобы вставить тег ID3, один простой подход заключался бы в расширении файла с помощью ftruncate() , mmap файла, затем memmove() содержимое вниз немного. Это, однако, уничтожит файл, если ваша программа выйдет из строя во время ее работы. Вы также можете скопировать содержимое файла в новый файл – это другое место, где mmap () действительно светит; вы можете просто mmap() старый файл, а затем скопировать все свои данные в новый файл с помощью single write() .

Короче говоря, mmap() отлично работает, если вы делаете большое количество ввода-вывода в терминах общего количества переданных байтов; это связано с тем, что оно уменьшает количество необходимых копий и может значительно сократить количество записей ядра, необходимых для чтения кэшированных данных. Однако mmap() требует минимум двух поездок в kernel ​​(три, если вы очищаете отображение, когда закончите!) И выполняет некоторые сложные вычисления внутреннего ядра, и поэтому фиксированные накладные расходы могут быть высокими.

read() с другой стороны, включает дополнительную копию памяти в память и, таким образом, может быть неэффективна для больших операций ввода-вывода, но прост, поэтому фиксированные накладные расходы относительно низки. Короче говоря, используйте mmap() для больших объемных pread() ввода-вывода и read() или pread() для одноразовых небольших входов / выходов.

Не мешайте mmap если ваш код не связан с ЦП, особенно из-за большого количества мелких чтений и записей. mmap может показаться приятным, но это не удивительно, почему не все используют эту альтернативу, как это выглядит.

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

Update0

Чтение связанного с вопросом находит этот ответ, который поддерживает мой опыт:

  • mmap () против блоков чтения

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

Как уже упоминалось, если вы обрабатываете много дисков ввода-вывода данных, вероятно, в любом случае будет доминировать над вашей обработкой. mmap может быть быстрее, чем читать, но для разумных реализаций, скорее всего, это не так намного быстрее, особенно на сегодняшнем оборудовании, которое постоянно растет быстрее и быстрее, в то время как дисковые накопители застряли в 7200 и 10000 об / мин в течение многих лет.

Итак, идите с mmap и сделайте свой код легким и аккуратным.

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

 int ftruncate(int fildes, off_t length); int truncate(const char *path, off_t length); 

определенный в unistd.h , который можно использовать для усечения файла до указанной длины. Таким образом, вы можете легко

  • найти, где начинается кадр ID3-тегов (не знаю, можете ли вы легко вычислить его, просто прочитав заголовок файла MP3, но я думаю, да)
  • сохранить смещение
  • закрыть файл
  • обрезать файл с предоставленной функцией
  • откройте файл в добавлении двоичного режима и напишите новые tags

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