Балансировка файлов, сопоставленных с памятью, для данных, размер которых превышает объем оперативной памяти

#c# #memory-mapped-files

Вопрос:

У нас есть очень большие файлы данных, например, предположим, что объем 3D с размером матрицы 2048×2048 с глубиной среза 20000.

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

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

Поэтому я решил взглянуть на файлы, сопоставленные с памятью…

Когда я теперь создаю файл с отображением памяти (чтение и запись) и просматриваю фрагмент за фрагментом данные и получаю доступ к данным с помощью ReadOnlySpan<> в цикле for, вся память потребляется, и вы видите, что линейная память процесса растет, пока не будет израсходована вся память. После этого система переходит к обмену, больше никогда не выходя. Это даже так, что система зависает, и только перезагрузка помогает восстановиться.

Таким образом, очевидно, что MM-файлы не балансируют сами по себе, и когда дело доходит до того, что мы достигаем максимума оперативной памяти, система истощена и явно беспомощна.

Вопрос в том, могу ли я контролировать, когда система выполняет обмен, и сообщать, какие регионы можно поменять местами? Или у вас есть предложения по лучшим подходам?

Я создал пример, который делает следующее

  1. Он создает объем в качестве входных данных
  2. Он создает один или несколько процессоров, которые просто добавляют по 10 к каждому срезу в каждом процессоре.
  3. Так как все создается только при доступе, то параллельно.Для перебирает срезы и запрашивает результирующий срез. В зависимости от количества N процессоров в цепочке каждый фрагмент результата должен иметь значение (N 1)*10.

Смотрите здесь: Пример кода

Обновление [26.09.2021]: Мои последние исследования показали, что запущены два независимых процесса. Один процесс (мой процесс) — это запись или считывание данных в файлы, сопоставленные с памятью. Другой системный процесс пытается управлять памятью. В то время как объем памяти растет, система, очевидно, очень лениво сбрасывает данные в файл, сопоставленный с резервной памятью, или в файл подкачки.

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

Я попробовал несколько вещей:

  1. VirtualUnlock позволяет мне помечать регионы как доступные для просмотра в диспетчере памяти.
  2. Вы можете вызвать сброс на ViewAccessor.
  3. Поскольку рабочий набор все еще показывает высокую загрузку памяти, я вызываю EmptyWorkingSet, чтобы очистить рабочий набор, который немедленно возвращается.

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

Мне нужно поискать еще…

Комментарии:

1. Итак, вы создаете файл с отображением памяти CreateFromFile с помощью , последовательно считываете его, и система начинает обмениваться данными, когда физическая память исчерпана? Я бы ожидал, что ОС просто удалит старые страницы, так как они не должны быть грязными. Но я не очень хорошо разбираюсь в таких деталях. Возможно, был бы полезен воспроизводимый пример?

2. @JonasH: Да, это то, что я бы предположил, но если достигнут предел оперативной памяти, он переходит только в режим обмена. Я постараюсь создать пример, так как я не все описал. Пример займет некоторое время.

3. @JonasH: Я добавил пример aist и обновил сообщение.

4. Можете ли вы воссоздать проблему с минимальным примером? Если вы запишете 91 ГБ целых чисел в файл и прочитаете их с помощью класса MMF, вы все равно получите обмен?

5. @asaf92: Это всегда зависит от того, сколько у меня оперативной памяти. Я протестировал его на машине с 256 ГБ, 80 ГБ и 64 ГБ. Все они показывают одну и ту же проблему. Если потребляемая память моих MM-файлов достигает предела оперативной памяти, проблема становится очевидной. Я бы предположил некоторую зигзагообразную кривую, когда система прекращает выделение, записывает данные на диск и продолжает обработку, но этого не происходит.