2016-03-09 1 views
4

Я хочу записать файл журнала, неструктурированный формат (по одной строке за раз), используя mmap (для скорости). Какая самая лучшая процедура? Я открываю пустой файл, truncate - 1 размер страницы (напишите пустую строку для изменения размера файла?), Затем mmap - и повторите, когда mmaped area заполнен?mmap для записи последовательного файла журнала для скорости?

Обычно я использую mmap для записи структур фиксированного размера, как правило, только по одной странице за раз, однако это для записи файлов журнала (где угодно от 0,5 до 10 ГБ) с использованием mmap, но не уверен, что лучше всего после первого mmaped область заполнена - munmap, изменить размер файла truncate и mmap следующая страница?

При записи журналов в область памяти, я бы отслеживал размер, и msync, что такое правильная обработка, как только я доберусь до конца отображаемой области памяти?

Предположим, мне никогда не нужно возвращаться или перезаписывать существующие данные, поэтому я только пишу новые данные в файл.

Q1: Когда я добираюсь до конца отображаемой области, я делаю , ftruncate файл для изменения размера другой страницы и mmap на следующей странице?

Q2: Есть ли стандартный способ предугадать и подготовить следующую страницу в памяти для следующей записи? Сделайте это в другом потоке, когда мы приблизимся к концу отображаемой области?

Q3: I madvise для последовательного доступа?

Это для обработки данных в реальном времени с требованием хранить файл журнала - в настоящее время я просто пишу в файл. Файл журнала неструктурирован, текстовый формат, на основе строки.

Это для linux/C++/c опционально тестирование на Mac (так что не переназначить [?]).

Любые ссылки/указатели на лучшие практики оценили.

ответ

17

Я написал свой диплом бакалавра по поводу сравнения fwrite VS mmap («Эксперимент по измерению эффективности обмена между традиционными вводами и файлами с памятью»). Прежде всего, для записи вам не нужно искать файлы с отображением памяти, особенно для больших файлов. fwrite полностью прекрасен и почти всегда превосходит подходы, используя mmap. mmap даст вам максимальную производительность для параллельного чтения данных; для последовательных данных, записывающих ваше реальное ограничение с fwrite - это ваше оборудование.


В моих примерах remapSize является начальным размером файла и размера, с помощью которого файл будет увеличен на каждое переназначении. fileSize отслеживает размер файла, mappedSpace представляет размер текущего mmap (его длина), alreadyWrittenBytes - это байты, которые уже были записаны в файл.

Вот пример инициализационные:

void init() { 
    fileDescriptor = open(outputPath, O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0600); // Open file 
    result = ftruncate(fileDescriptor, remapSize); // Init size 
    fsync(fileDescriptor); // Flush 
    memoryMappedFile = (char*) mmap64(0, remapSize, PROT_WRITE, MAP_SHARED, fileDescriptor, 0); // Create mmap 
    fileSize = remapSize; // Store mapped size 
    mappedSpace = remapSize; // Store mapped size 
} 

Q1 объявления:

Я использовал "Unmap-Remap" -mechanism.

Unmap

  • первые приливы (msync)
  • , а затем в памяти снимает отображение файла, отображенного.

Это может выглядеть следующим образом:

void unmap() { 
    msync(memoryMappedFile, mappedSpace, MS_SYNC); // Flush 
    munmap(memoryMappedFile, mappedSpace) 
} 

Для Переоформления, у вас есть выбор, чтобы переназначить весь файл или только недавно добавленный часть.

переназначить в основном

  • увеличивает размер файла
  • создает новую карту пам

Пример реализации для полного переназначить:

void fullRemap() { 
    ftruncate(fileDescriptor, mappedSpace + remapSize); // Make file bigger 
    fsync(fileDescriptor); // Flush file 
    memoryMappedFile = (char*) mmap64(0, mappedSpace + remapSize, PROT_WRITE, MAP_SHARED, fileDescriptor, 0); // Create new mapping on the bigger file 
    fileSize += reampSize; 
    mappedSpace += remapSize; // Set mappedSpace to new size 
} 

Пример реализации для малого переназначить :

void smallRemap() { 
    ftruncate(fileDescriptor, fileSize + remapSize); // Make file bigger 
    fsync(fileDescriptor); // Flush file 
    remapAt = alreadyWrittenBytes % pageSize == 0 
      ? alreadyWrittenBytes 
      : alreadyWrittenBytes - (alreadyWrittenBytes % pageSize); // Adjust remap location to pagesize 
    memoryMappedFile = (char*) mmap64(0, fileSize + remapSize - remapAt, PROT_WRITE, MAP_SHARED, fileDescriptor, remapAt); // Create memory-map 
    fileSize += remapSize; 
    mappedSpace = fileSize - remapAt; 
} 

Существует mremap function там, но он утверждает,

Этого вызов является Linux-конкретным, и не должен использоваться в программах , переносимые.

Q2 объявления:

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

Q3 объявления:

Вы можете использовать madvise с флагом MADV_SEQUENTIAL, но имейте в виду, что это не навязывает ядро ​​читать дальше, но только советы его.

Excerp образуют man:

Это может вызвать ядро ​​агрессивно упреждающего чтения

Личный вывод:

Не используйте mmap для последовательной записи данных , Это просто вызовет намного больше накладных расходов и приведет к гораздо более «неестественному» коду, чем простому алографическому письму, используя fwrite.

Использование mmap для случайного доступа к файлам.

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

+0

'mmap' действительно не имеет промежуточный буфер и записывает данные непосредственно по запросу (то же самое для' write' и друзей), космические APIs вещь пользователь обычно избегают. Это также позволяет сократить количество системных вызовов, которые дорого стоить. – edmz

+0

Можете ли вы предоставить контрольные данные для 'mmap' vs' fwrite', а также 'write'? –

+0

Я сравнивал только 'mmap' с' fwrite', с дополнительными параметрами, такими как распараллеливание и боковая нагрузка, однако тезис в настоящее время не полностью завершен и опубликован, поэтому я не уверен, разрешено ли мне публиковать результаты на данный момент , –

3

используя mmap (для скорости). Какая самая лучшая процедура?

Не используйте mmap, используйте write. Шутки в сторону. Почему люди всегда думают, что mmap каким-то образом волшебным образом ускорит процесс?

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

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

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

Для дальнейшей разработки я буду ссылаться на сам Линус Торвальдс:

http://lkml.iu.edu/hypermail/linux/kernel/0004.0/0728.html

< В статье [email protected]> Пол Barton-Davis писал: >

Я был очень уныл, чтобы найти, что на моей системе был установлен mmap/mlock 3 TIMES до тех пор, пока читаемое решение. Мне показалось , что mmap/mlock должен быть не менее быстрым, чем чтение. Комментарии отправлены .

Люди любят ММАП() и другие способы, чтобы играть с таблицами страниц для оптимизируют прочь операцию копирования, а иногда это стоит.

ОДНАКО, игра в игры с виртуальной картографией памяти очень дорого сама по себе. У этого есть целый ряд вполне реальных недостатков, которые люди склонны игнорировать, потому что копирование памяти рассматривается как нечто очень медленное, и иногда оптимизация этой копии рассматривается как очевидное улучшение .

Downsides к ттар:

  • довольно заметная установка и демонтаж затрата. И я имею в виду заметный. Это похоже на следующие таблицы страниц, чтобы очистить все чисто. Это бухгалтерский учет для ведения списка всех сопоставлений. Это флеш TLB, необходимый после разборки.

  • страница неисправность стоит дорого. Вот как заполняется картирование, и это довольно медленно.

расквитаться из ттар:

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

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

И автоматический обмен, очевидно, дело это ..

Но ваш тест-люкс (просто скопировать данные один раз), вероятно, pessimal для ттар().

Линус

+0

странно последние результаты см. Mmap() _reads_ как [совпадение] (https://jvns.ca/blog/2014/05/12/computers-are-fast/) или [превосходит] (https: //web.archive .org/web/20170711083542/http: //blog.burntsushi.net/ripgrep) read() performance _on large files_. Поэтому есть случаи, когда это может помочь. – sourcejedi

+0

@sourcejedi: Ссылка? Какой размер блока они использовали? Также см. Https://eklitzke.org/efficient-file-copying-on-linux – datenwolf

+0

[64K] (https://github.com/jvns/howcomputer/blob/master/bytesum.c) в ссылке 1. ripgrep (ссылка 2) в настоящее время используется [8K] (https://github.com/BurntSushi/ripgrep/blob/master/src/search_stream.rs#L20). Так хорошо, спасибо. Ваша ссылка путается, поскольку она объясняет график с точки зрения readahead, но данные графика предназначены для полностью виртуального (in-ram) устройства/dev/zero. – sourcejedi