Mmap, выделяет огромные объемы памяти
У меня есть некоторые огромные файлы, которые мне нужны для синтаксического анализа, и люди рекомендуют mmap, потому что это должно избегать выделения всего файла в памяти.
Но, глядя на "верх", похоже, что я открываю весь файл в памяти, поэтому я думаю, что я должен что-то делать неправильно. 'top show > 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;
}
Ответы
Ответ 1
Нет, что вы делаете, это сопоставление файла в памяти. Это отличается от фактического чтения файла в памяти.
Если бы вы его прочитали, вам пришлось бы перенести все содержимое в память. Сопоставляя его, вы позволяете операционной системе обрабатывать его. Если вы попытаетесь прочитать или записать в местоположение в этой области памяти, ОС сначала загрузит соответствующий раздел. Он не будет загружать весь файл, если не нужен весь файл.
Вот где вы получаете выигрыш в производительности. Если вы сопоставляете весь файл, но только изменяете один байт, а затем удаляете его, вы обнаружите, что там не очень много операций ввода-вывода.
Конечно, если вы касаетесь каждого байта в файле, то да, он будет загружен в какой-то момент, но не обязательно в физическую RAM сразу. Но это дело, даже если вы загрузите весь файл вперед. ОС будет заменять части ваших данных, если не хватает физической памяти, чтобы содержать все это вместе с другими процессами в системе.
Основными преимуществами отображения памяти являются:
- вы откладываете чтение разделов файла до тех пор, пока они не понадобятся (и, если они никогда не нужны, они не загружаются). Поэтому при загрузке всего файла нет больших авансовых затрат. Он амортизирует стоимость загрузки.
- Запись автоматизирована, вам не нужно записывать каждый байт. Просто закройте его, и ОС запишет измененные разделы. Я думаю, что это также происходит, когда память также заменяется (в ситуациях с низкой физической памятью), так как ваш буфер является просто окном в файл.
Имейте в виду, что существует скорее всего разрыв между использованием вашего адресного пространства и использованием физической памяти. Вы можете выделить адресное пространство 4G (в идеале, хотя могут быть ограничения по ОС, BIOS или аппаратным средствам) на 32-разрядной машине с 1 ГБ ОЗУ. ОС обрабатывает пейджинг на диск и с него.
И чтобы ответить на ваш дальнейший запрос для разъяснения:
Просто уточнить. Итак, если мне нужен весь файл, mmap фактически загрузит весь файл?
Да, но он может быть не в физической памяти сразу. ОС будет заменять биты обратно в файловую систему, чтобы вносить новые биты.
Но это также сделает это, если вы прочитали весь файл вручную. Разница между этими двумя ситуациями заключается в следующем.
С файлом, считываемым в память вручную, ОС будет заменять части вашего адресного пространства (могут включать данные или не могут) в файл подкачки. И вам нужно будет вручную переписать файл, когда вы закончите с ним.
С отображением памяти вы фактически сказали ему использовать исходный файл в качестве дополнительной области подкачки только для этого файла/памяти. И, когда данные записываются в эту область подкачки, это немедленно влияет на фактический файл. Поэтому нет необходимости вручную переписывать что-либо, когда вы закончите, и не влияете на нормальный обмен (обычно).
Это действительно окно для файла:
![memory mapped file image]()
Ответ 2
Вы также можете использовать 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 REALTIME IEEE Std 1003.1, 2004. И константы будут POSIX_MADV_SEQUENTIAL и POSIX_FADV_SEQUENTIAL.
Ответ 3
top
имеет много столбцов, связанных с памятью. Большинство из них основаны на размере пространства памяти, сопоставленного с процессом; включая любые разделяемые библиотеки, помененную оперативную память и помеченное пространство.
Проверьте столбец RES
, это связано с используемой физической оперативной памятью. Я думаю (но не уверен) он будет включать оперативную память, используемую для "кэширования" файла mmap'ped
Ответ 4
Вам, возможно, предложили неправильный совет.
Файлы с отображением памяти (mmap) будут использовать все больше и больше памяти при их анализе. Когда физическая память становится низкой, ядро будет деактивировать разделы файла из физической памяти на основе его LRU (наименее недавно использованного) алгоритма. Но LRU также является глобальным. LRU также может заставить другие процессы обменивать страницы на диск и уменьшать кеш диска. Это может оказать серьезное негативное влияние на производительность других процессов и системы в целом.
Если вы линейно читаете файлы, например, подсчитываете количество строк, mmap является плохим выбором, так как он будет заполнять физическую память перед выпуском памяти обратно в систему. Было бы лучше использовать традиционные методы ввода-вывода, которые передают или читают в блоке за раз. Таким образом, память может быть сразу выпущена.
Если вы произвольно обращаетесь к файлу, mmap - это хороший выбор. Но это не оптимально, так как вы все равно будете полагаться на общий алгоритм LRU ядра, но его быстрее использовать, чем писать механизм кэширования.
В общем, я бы никогда не рекомендовал, чтобы кто-либо использовал mmap, за исключением некоторых крайних случаев производительности - например, для доступа к файлу из нескольких процессов или потоков одновременно или когда файл является небольшим по отношению к количеству бесплатных память.
Ответ 5
"выделить весь файл в памяти" объединяет две проблемы. Во-первых, сколько виртуальной памяти вы выделяете; другой - какие части файла считываются с диска в память. Здесь вы выделяете достаточно места для хранения всего файла. Тем не менее, только те страницы, которые вы касаетесь, будут фактически изменены на диске. И они будут правильно изменены независимо от того, что происходит с процессом, как только вы обновили байты в памяти, которые были выделены для mmap. Вы можете выделять меньше памяти, сопоставляя только часть файла за раз, используя параметры "размер" и "смещение" mmap. Затем вам нужно самому управлять окном в файл, сопоставляя и разбирая его, возможно, перемещая окно через файл. Выделение большого объема памяти занимает значительное время. Это может привести к неожиданным задержкам в приложении. Если ваш процесс уже интенсивно используется в памяти, виртуальная память, возможно, стала фрагментированной, и в то время, когда вы спрашиваете, может оказаться невозможным найти достаточно большой фрагмент для большого файла. Поэтому, возможно, необходимо попытаться сделать картографирование как можно раньше или использовать некоторую стратегию для хранения достаточно большого объема памяти до тех пор, пока она вам не понадобится.
Однако, видя, что вам нужно проанализировать файл, почему бы не избежать этого полностью, организовав парсер для работы с потоком данных? Тогда вам больше всего понадобится некоторая перспектива и некоторая история, вместо того, чтобы отображать дискретные куски файла в память.
Ответ 6
Система, безусловно, попытается поместить все ваши данные в физическую память. То, что вы сохраните, - это своп.
Ответ 7
Вам нужно указать размер, меньший, чем общий размер файла в вызове mmap, если вы не хотите, чтобы весь файл сразу отображался в памяти. Используя параметр смещения и меньший размер, вы можете отобразить "окна" большего файла, по одному за раз.
Если ваш синтаксический анализ представляет собой один проход через файл с минимальным обратным просмотром или просмотром, то вы фактически ничего не получите, используя mmap вместо стандартного буферизированного ввода-вывода. В примере, который вы указали для подсчета новых строк в файле, было бы так же быстро сделать это с помощью fread(). Я предполагаю, что ваш фактический анализ более сложный.
Если вам нужно читать из более чем одной части файла за раз, вам придется управлять несколькими областями mmap, которые могут быстро усложниться.
Ответ 8
Немного не по теме.
Я не совсем согласен с ответом Марка. На самом деле mmap
быстрее, чем fread
.
Несмотря на использование системного дискового буфера, fread
также имеет внутренний буфер, и, кроме того, данные будут скопированы в предоставленный пользователем буфер, как он называется.
Наоборот, mmap
просто верните указатель на системный буфер. Таким образом, сохраняется сохранение двух копий.
Но использование mmap
немного опасно. Вы должны убедиться, что указатель никогда не выходит из файла, или будет ошибка сегмента. Хотя в этом случае fread
просто возвращает ноль.