2009-12-29 1 views
13

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

Но, глядя на «верх», похоже, что я открываю весь файл в памяти, поэтому я думаю, что я должен делать что-то неправильно. 'top shows> 2.1 gig'

Это фрагмент кода, который показывает, что я делаю.

Благодаря

#include <stdio.h> 
#include <stdlib.h> 
#include <err.h> 
#include <fcntl.h> 
#include <sysexits.h> 
#include <unistd.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <sys/mman.h> 
#include <cstring> 
int main (int argc, char *argv[]) { 
    struct stat sb; 
    char *p,*q; 
    //open filedescriptor 
    int fd = open (argv[1], O_RDONLY); 
    //initialize a stat for getting the filesize 
    if (fstat (fd, &sb) == -1) { 
    perror ("fstat"); 
    return 1; 
    } 
    //do the actual mmap, and keep pointer to the first element 
    p =(char *) mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 
    q=p; 
    //something went wrong 
    if (p == MAP_FAILED) { 
    perror ("mmap"); 
    return 1; 
    } 
    //lets just count the number of lines 
    size_t numlines=0; 
    while(*p++!='\0') 
    if(*p=='\n') 
     numlines++; 
    fprintf(stderr,"numlines:%lu\n",numlines); 
    //unmap it 
    if (munmap (q, sb.st_size) == -1) { 
    perror ("munmap"); 
    return 1; 
    } 
    if (close (fd) == -1) { 
    perror ("close"); 
    return 1; 
    } 
    return 0; 
} 
+0

@monkeyking, правильное закрытие для кода-pre/pre-/code, а не сообщение :-) Исправлены теги кода для вас. – paxdiablo

+0

Ahh благодарит миллион! Как насчет #include Я не мог поместить их в образец кода – monkeyking

+0

Отметьте весь блок, затем используйте CTRL-K - это будет отступ четырьмя пробелами. Я сделал это сейчас, и вы сможете увидеть stdio include. – paxdiablo

ответ

39

Нет, что вы делаете это отображение файла в память. Это отличается от фактического чтения файла в памяти.

Чтобы вы его прочитали, вам нужно будет перенести все содержимое в память. Сопоставляя его, вы позволяете операционной системе обрабатывать его. Если вы попытаетесь прочитать или записать в местоположение в этой области памяти, ОС сначала загрузит соответствующий раздел. Это будет не загрузить весь файл, если не нужен весь файл.

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

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

Основные преимущества отображения памяти являются:

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

Имейте в виду, что существует скорее всего разрыв между использованием вашего адресного пространства и использованием физической памяти. Вы можете выделить адресное пространство 4G (в идеале, хотя могут быть ограничения по ОС, BIOS или аппаратным средствам) на 32-разрядной машине с 1 ГБ ОЗУ. ОС обрабатывает пейджинг на и с диска.

И ответить на ваш дальнейший запрос о разъяснении:

Просто для уточнения. Итак, если мне нужен весь файл, mmap фактически загрузит весь файл?

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

Но это также сделает это, если вы прочитали весь файл вручную. Разница между этими двумя ситуациями заключается в следующем.

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

Имея карту памяти, вы эффективно сказали ей использовать исходный файл в качестве дополнительной области подкачки только для этого файла/памяти. И, когда данные записываются в , эта область подкачки немедленно влияет на фактический файл. Поэтому нет необходимости вручную переписывать что-либо, когда вы закончите, и не влияете на нормальный обмен (обычно).

Это действительно просто окно в файл:

                                        memory mapped file image

+0

Просто уточнить. Итак, если мне нужен весь файл, mmap фактически загрузит весь файл? – monkeyking

+0

Да, см. Обновление. – paxdiablo

+0

@paxdiablo, не могли бы вы также прояснить это: «Когда файл будет загружен в память вручную, ОС будет заменять части вашего адресного пространства (могут включать данные или не могут) в файл подкачки». Вы имели в виду, что если мы прочитаем (2) весь файл в памяти -> напишите (2) некоторые данные -> закройте (2) it (fsync (2) при необходимости), файл не будет содержать последних изменений ? Или использовать следующую схему? read (2) -> некоторые изменения -> напишите (2) весь файл. – dshil

0

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

+0

неправильно. VM будет использовать RAM, чтобы сделать файл доступным; но он будет заменен, как только будет некоторое давление памяти. Это почти так же, как просто использование ОЗУ в качестве кеша для файла. – Javier

+0

Неправильно. Он никогда не будет использовать пространство подкачки для отображения только для чтения. Он будет делать ввод-вывод для его замены, но вы не будете использовать пробел. – bmargulies

3

top содержит много столбцов, связанных с памятью. Большинство из них основаны на размере пространства памяти, сопоставленного с процессом; включая любые разделяемые библиотеки, помененную оперативную память и mmapped-пространство.

Проверьте колонку RES, это связано с используемой физической оперативной памятью. Я думаю (но не уверен) он будет включать ОЗУ, используемую для «кэширования» файла mmap'd.

1

«выделить весь файл в памяти», объединяет две проблемы. Во-первых, сколько виртуальной памяти вы выделяете; другой - какие части файла считываются с диска в память. Здесь вы выделяете достаточно места для хранения всего файла. Тем не менее, только те страницы, которые вы касаетесь, будут фактически изменены на диске. И они будут правильно изменены независимо от того, что происходит с процессом, как только вы обновили байты в памяти, которые были выделены для mmap. Вы можете выделять меньше памяти, сопоставляя только часть файла за раз, используя параметры «размер» и «смещение» mmap. Затем вам нужно самому управлять окном в файл, сопоставляя и разбирая его, возможно, перемещая окно через файл. Выделение большого объема памяти занимает значительное время. Это может привести к неожиданным задержкам в приложении. Если ваш процесс уже интенсивно используется в памяти, виртуальная память, возможно, стала фрагментированной, и в то время, когда вы спрашиваете, может оказаться невозможным найти достаточно большой фрагмент для большого файла. Поэтому, возможно, необходимо попытаться сделать картографирование как можно раньше или использовать некоторую стратегию для хранения достаточно большого объема памяти до тех пор, пока она вам не понадобится.

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

2

Возможно, вам предложили неправильный совет.

Файлы с отображением карты памяти (mmap) будут использовать все больше и больше памяти при прохождении через них. Когда физическая память становится низкой, ядро ​​будет деактивировать разделы файла из физической памяти на основе его LRU (наименее недавно использованного) алгоритма. Но LRU также является глобальным.LRU также может заставить другие процессы обменивать страницы на диск и уменьшать кеш диска. Это может оказать серьезное негативное влияние на производительность других процессов и системы в целом.

Если вы линейно просматриваете файлы, например, подсчитываете количество строк, mmap является плохим выбором, так как он заполняет физическую память перед выпуском памяти обратно в систему. Было бы лучше использовать традиционные методы ввода-вывода, которые передают или читают в блоке за раз. Таким образом, память может быть немедленно выпущена.

Если вы случайно обращаетесь к файлу, mmap - это хороший выбор. Но это не оптимально, так как вы все равно будете полагаться на общий алгоритм LRU ядра, но его быстрее использовать, чем писать механизм кэширования.

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

+1

Мех. Вы можете сделать около 10 поисков дерева с помощью mmap за время, затрачиваемое на блокирование структуры дерева B + по блоку. –

+0

Не обязательно верно. Производительность первого считывания IO будет практически идентичной (для всех практических целей) между mmap и pread - оба должны прочитать ее с носителя. Проблема заключается в последующих чтениях. Mmap будет использовать алгоритм LRU с выделением памяти ядра LRU, чтобы решить, какие страницы будут отображаться. С Pread будет подсистема IO решить, какие блоки удалить из кеша (если есть). Ни один из подходов не является высокоэффективным с точки зрения освобождения неиспользуемых ресурсов памяти. Таким образом, приложение, основанное на mmap, может снизить производительность и эффективность всей системы за счет хранения ресурсов памяти. – tgiphil

+1

Вы не считаете несколько тысяч циклов процессора, потраченных впустую каждым системным вызовом. mmap быстрее загружается. –

0

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

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

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

0

Немного не по теме.

Я не совсем согласен с ответом Марка. На самом деле mmap быстрее, чем fread.

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

Наоборот, mmap просто верните указатель на буфер системы. Таким образом, есть с двумя копиями-копиями, сохраняющими.

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

+0

Я действительно сделал бенчмаркинг, который показывает, что (в Mac OS X, во всяком случае) практически нет разницы в пропускной способности между оконными mmap и fread для прямого чтения. Да, используя библиотеку высокого уровня, данные копируются (до трех раз), но время копирования данных незначительно по сравнению с фактическим временем ввода-вывода. Обычно я использую интерфейс самого высокого уровня. –

+1

@Mark: Соглашайтесь с вами, когда файл будет прочитан в первый раз. Однако, если программа читает файл более одного раза или программа запускается повторно (например, веб-сервер), будет огромная разница. (Изменение 'fread' на' mmap' сделало всю программу на 50% быстрее в одном из моих опытов) – iamamac

+0

Особенно, если учитывать, что 'fseek' +' fread' всегда считывает полный размер буфера для любого заданного размера. –

4

Вы также можете использовать fadvise (2) (и madvise (2), см. Также posix_fadvise & posix_madvise), чтобы отметить mmaped файл (или его части) как прочитанный-раз.

#include <sys/mman.h> 

int madvise(void *start, size_t length, int advice); 

Рекомендации указывается в параметре советы, которые могут быть

MADV_SEQUENTIAL 

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

Переносимость: posix_madvise и posix_fadvise является частью опции ADVANCED в реальном времени IEEE Std 1003.1, 2004 и константы будут POSIX_MADV_SEQUENTIAL и POSIX_FADV_SEQUENTIAL.

Смежные вопросы