Подтвердить что ты не робот

Mmap() vs read()

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

Update0

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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