проблема с 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;
}

person monkeyking    schedule 29.12.2009    source источник
comment
@monkeyking, правильное закрытие для code-pre — это /pre-/code, а не post :-) Исправил для вас теги кода.   -  person paxdiablo    schedule 29.12.2009
comment
Ааа спасибо миллион! А как насчет #include? Я не смог включить их в пример кода.   -  person monkeyking    schedule 29.12.2009
comment
Отметьте весь блок, затем используйте CTRL-K — это сделает отступ на четыре пробела. Я сделал это сейчас, и вы должны увидеть включение stdio.   -  person paxdiablo    schedule 29.12.2009


Ответы (8)


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

person paxdiablo    schedule 29.12.2009
comment
Просто для ясности. Итак, если мне нужен весь файл, mmap действительно загрузит весь файл? - person monkeyking; 29.12.2009
comment
@paxdiablo, не могли бы вы также уточнить это: когда файл считывается в память вручную, ОС заменяет части вашего адресного пространства (может включать данные или нет) в файл подкачки. Вы имели в виду, что если мы прочитаем(2) весь файл в память -> запишем(2) некоторые данные -> закроем(2) его (fsync(2) при необходимости), файл не будет содержать последних изменений? Или следует использовать следующую схему? read(2) -> некоторые изменения -> write(2) весь файл. - person dshil; 21.02.2018

Вы также можете использовать 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 1003.1, 2004. Константы будут POSIX_MADV_SEQUENTIAL и POSIX_FADV_SEQUENTIAL.

person osgx    schedule 07.02.2010

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

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

person Javier    schedule 29.12.2009

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

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

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

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

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

person tgiphil    schedule 29.12.2009
comment
Мех. Вы можете выполнить около 10 поисковых запросов по дереву с помощью mmap за время, необходимое для предварительного чтения структуры B+ поблочно. - person Zan Lynx; 16.07.2010
comment
Не обязательно правда. Производительность первого чтения IO будет почти одинаковой (для всех практических целей) между mmap и pread — оба должны читать его с носителя. Проблема заключается в последующих чтениях. Mmap будет использовать алгоритм LRU вытеснения памяти ядра, чтобы решить, какие страницы отображать. С Pread подсистема ввода-вывода будет решать, какие блоки удалить из кеша (если есть). Ни один из подходов не является высокоэффективным с точки зрения высвобождения неиспользуемых ресурсов памяти. Таким образом, приложение, использующее mmap, может снизить производительность и эффективность всей системы из-за экономии ресурсов памяти. - person tgiphil; 22.07.2010
comment
Вы не считаете несколько тысяч циклов ЦП, потраченных впустую на каждый системный вызов. mmap загружается быстрее. - person Zan Lynx; 12.07.2011
comment
mmap также вызывает системные вызовы, это просто под капотом. Процессор и ядро ​​перехватывают доступ к памяти для неотображенной страницы (при условии, что она еще не загружена) и вызывают необходимые системные функции для помещения страницы в память. Даже в высокопроизводительных базах данных используется блочный доступ, а не mmap, поэтому они могут контролировать и эффективно использовать память. - person tgiphil; 04.11.2011
comment
Как только страницы mmap загружены, система больше не участвует. А высокопроизводительные базы данных не так уж и высокопроизводительны. Если вам нужна высокая производительность, посмотрите на memcache. И он не использует блочный доступ. - person Zan Lynx; 04.11.2011
comment
Две проблемы с этим комментарием: mmap загружает страницы по запросу (или для упреждающего чтения), он не загружает весь файл в память сразу! Так что есть больше участия системы. Memcache предназначен для системы кэширования распределенной памяти и поэтому не применим к этому обсуждению. - person tgiphil; 18.12.2011

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

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

person Permaquid    schedule 29.12.2009

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

person bmargulies    schedule 29.12.2009
comment
неправильный. виртуальная машина будет использовать оперативную память, чтобы сделать файл доступным; но он будет заменен, как только возникнет нехватка памяти. Это почти то же самое, что просто использовать оперативную память в качестве кеша для файла. - person Javier; 29.12.2009
comment
Неправильный. Он никогда не будет использовать пространство подкачки для отображения только для чтения. Он будет выполнять ввод-вывод, чтобы заменить его, но вы не будете использовать пространство. - person bmargulies; 29.12.2009

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

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

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

person Mark Bessey    schedule 29.12.2009

Немного не в тему.

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

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

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

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

person iamamac    schedule 29.12.2009
comment
На самом деле я провел бенчмаркинг, который показывает, что (по крайней мере, в Mac OS X) почти нет разницы в пропускной способности между оконным mmap и fread для прямого чтения. Да, при использовании высокоуровневой библиотеки данные копируются (до трех раз), но время копирования данных ничтожно мало по сравнению с фактическим временем ввода-вывода. Я обычно использую подходящий интерфейс самого высокого уровня. - person Mark Bessey; 02.01.2010
comment
@Mark: согласен с вами, когда файл читается в первый раз. Однако, если программа читает файл более одного раза или программа запускается несколько раз (например, веб-сервер), разница будет огромной. (Из моего опыта изменение fread на mmap сделало всю программу на 50% быстрее) - person iamamac; 03.01.2010
comment
Особенно, если учесть, что fseek+fread всегда считывает полный размер буфера для любого заданного размера. - person Patrick Schlüter; 04.11.2010