проблема 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++!='')
    if(*p=='n')
      numlines++;
  fprintf(stderr,"numlines:%lun",numlines);
  //unmap it
  if (munmap (q, sb.st_size) == -1) {
    perror ("munmap");
    return 1;
  }
  if (close (fd) == -1) {
    perror ("close");
    return 1;
  }
  return 0;
}
8 13

8 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

На самом деле это просто окно в файл:

                        памяти сопоставленных файлов изображений

Вы также можете использовать 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-это часть расширенного реального времени вариант IEEE Std 1003.1, 2004. И константы будут POSIX_MADV_SEQUENTIAL и POSIX_FADV_SEQUENTIAL.

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

Проверьте столбец RES, это связано с физической оперативной памятью, используемой в настоящее время. Я думаю (но не уверен), что он будет включать оперативную память, используемую для "кэширования" файла mmap'ped

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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