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

Как переносить доступ к файлам с помощью mmap()

Мы экспериментируем с изменением SQLite, встроенной системы баз данных, использовать mmap() вместо обычных вызовов read() и write() для доступа файл базы данных на диске. Использование одного большого отображения для всего файл. Предположим, что файл достаточно мал, что у нас нет проблем найдя место для этого в виртуальной памяти.

Пока все хорошо. Во многих случаях использование mmap() кажется немного быстрее чем read() и write(). А в некоторых случаях гораздо быстрее.

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

  ftruncate();    // extend the database file on disk 
  munmap();       // unmap the current mapping (it now too small)
  mmap();         // create a new, larger, mapping

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

В Linux мы можем использовать нестандартный системный вызов mremap() вместо munmap()/mmap(), чтобы изменить размер отображения. Это, по-видимому, незначительные ошибки страницы.

ВОПРОС: Как это должно быть рассмотрено в других системах, таких как OSX, у которых нет mremap()?


В настоящее время мы имеем две идеи. И вопрос о каждом:

1) Создавайте сопоставления, большие, чем файл базы данных. Затем, когда расширяется  файл базы данных, просто вызовите ftruncate(), чтобы расширить файл на  диска и продолжать использовать одно и то же отображение.

Это было бы идеально и, кажется, работает на практике. Однако мы  беспокоился об этом предупреждении на странице руководства:

"Эффект изменения размера базового файла  сопоставление на страницах, которые соответствуют добавленным или удаленным регионам  файл не указан."

ВОПРОС: Это что-то, о чем мы должны беспокоиться? Или анахронизм  на данный момент?

2) При расширении файла базы данных используйте первый аргумент для mmap()  запросить сопоставление, соответствующее новым страницам базы данных  файл, расположенный сразу после текущего сопоставления в виртуальном  Память. Эффективное расширение исходного отображения. Если система  не может выполнить запрос о размещении нового картографирования сразу после  во-первых, вернитесь к munmap/mmap.

На практике мы обнаружили, что OSX неплохо относится к позиционированию  таким образом, этот трюк работает там.

ВОПРОС: если система действительно распределяет второе отображение сразу  после первого в виртуальной памяти, безопасно ли оно в конечном итоге  отмените их оба с помощью одного большого вызова munmap()?

4b9b3361

Ответ 1

  • Используйте fallocate() вместо ftruncate(), если это доступно. Если нет, просто откройте файл в режиме O_APPEND и увеличьте файл, записав некоторое количество нулей. Это значительно сокращает фрагментацию.

  • Используйте "Огромные страницы", если они доступны - это значительно сокращает накладные расходы при больших сопоставлениях.

  • pread()/pwrite()/pwritev()/preadv() с небольшим размером блока на самом деле не очень медленный. На самом деле гораздо быстрее, чем IO.

  • Ошибки ввода-вывода при использовании mmap() будут генерировать только segfault вместо EIO или так.

  • Большинство проблем производительности SQLite WRITE сконцентрированы в хорошем транзакционном использовании (т.е. вы должны отлаживать, когда COMMIT фактически выполняется).

Ответ 2

  • Я думаю, что # 2 - лучшее в настоящее время решение. В дополнение к этому на 64-битных системах вы можете явно создать свое сопоставление по адресу, который ОС никогда не выберет для сопоставления (например, 0x6000 0000 0000 0000 в Linux), чтобы избежать того, что ОС не может сразу разместить новое сопоставление после первого один.

  • Всегда можно отключить mutiple mappinsg с помощью одного вызова munmap. Вы даже можете отформатировать часть отображения, если хотите это сделать.