проблема mmap, выделяет огромные объемы памяти

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

Но рассмотрение 'вершины', действительно похоже, что я открываю весь файл в память, таким образом, я думаю, что должен делать что-то не так. 'лучшие шоу> 2,1 ГБ'

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

Спасибо

#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;
}
13
задан monkeyking 29 December 2009 в 03:48
поделиться

8 ответов

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.                        memory mapped file image

39
ответ дан 1 December 2019 в 17:42
поделиться

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

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

.
3
ответ дан 1 December 2019 в 17:42
поделиться

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

0
ответ дан 1 December 2019 в 17:42
поделиться

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

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

1
ответ дан 1 December 2019 в 17:42
поделиться

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

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

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

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

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

.
2
ответ дан 1 December 2019 в 17:42
поделиться

Небольшая тема.

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

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

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

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

.
0
ответ дан 1 December 2019 в 17:42
поделиться

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

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

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

.
0
ответ дан 1 December 2019 в 17:42
поделиться

Вы также можете использовать fadvise(2) (и madvise(2), см. также posix_fadvise & posix_madvise ), чтобы пометить mmaped file (или его части) как read-once.

#include <sys/mman.h> 

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

Совет указан в параметре advice, который может быть

MADV_SEQUENTIAL 

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

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

4
ответ дан 1 December 2019 в 17:42
поделиться