Как «скопировать» матрицу, не создавая временную матрицу в памяти, которая вызвала переполнение памяти?

9

Назначая матрицу в гораздо большую выделенную память, Matlab каким-то образом будет дублировать ее при «копировании», и если копируемая матрица будет достаточно большой, произойдет переполнение памяти. Это пример кода:

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
    parfor i=1:n
        slice_matrix(:,:,i)=gather(gpuArray(rand(500,500)));
    end
    main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end

Любой способ просто "разбить" slice_matrixна main_matбез накладных расходов? Заранее спасибо.

РЕДАКТИРОВАТЬ:

Переполнение произошло, когда main_matвыделено заранее. Если main_matинициализируется с main_mat=zeros(500,500,1);(меньший размер), переполнение не произойдет, но оно будет замедлено, так как распределение не будет выполнено до того, как в него будет назначена матрица. Это значительно снизит производительность по мере увеличения диапазона k.

Грегор Исак
источник
1
Что касается ваших циклов: для оптимизации рекомендуется установить внешний цикл в parforцикл . Кроме того, parforкопирует ваши данные каждому отдельному работнику, таким образом, предполагая, что 4 работника дублируют ваши данные четыре раза в оперативной памяти.
Адриан
1
Каковы ваши признаки того, что Matlab на самом деле дублирует память? Вы используете memoryфункцию? Задача-менеджер? Ошибка памяти от Matlab? По какой строке кода это происходит?
Элиаху Аарон
Как вы могли видеть, где я прокомментировал код, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)где возникает проблема переполнения памяти. Это проверено, когда я выделил main_matзаранее, это переполнится, если я не сделаю, это не будет. Matlab вернет сообщение об ошибке «Недостаточно памяти».
Грегор Исак
Ваша матрица 500x500x2000 помещается в памяти? Это ~ 4 Гб. См. Stackoverflow.com/q/51987892/7328782, чтобы узнать, почему ошибка нехватки памяти может произойти только при записи в массив.
Крис
Чтобы лучше понять вашу проблему, не могли бы вы вставить h=h+slice_matrix(end)перед main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(и инициализировать h с 0)? Я подозреваю, что эта добавленная строка уже вызовет проблемы с памятью.
Даниэль

Ответы:

4

Основная проблема заключается в том, что числа занимают больше места, чем нули. main_mat=zeros(500,500,2000);занимает мало ОЗУ, а main_mat = rand(500,500,2000);занимает много, независимо от того, используете ли вы графический процессор или парфор (фактически, парфор заставит вас использовать больше оперативной памяти). Так что это не противоестественный отек памяти. Следуя приведенной ниже ссылке Даниила, кажется, что присвоение нулей только создает указатели на память, а физическая память заполняется только при использовании матрицы для «чисел». Это управляется операционной системой. И это ожидается для Windows, Mac и Linux, либо вы делаете это с Matlab или другими языками, такими как C.

Юваль Харпаз
источник
Прямо сейчас я больше не понимаю MATLAB. Как только я набираю команды с zerosцелой виртуальной памятью, она фактически выделяется, но память не используется. whosпоказывает одинаковый размер для обеих матриц, в то время как моя ОС показывает разное потребление памяти. Я удалил свой комментарий, потому что ваш ответ определенно не ошибся.
Даниил
3
Я нашел кое-что объясняющее это: stackoverflow.com/questions/51987892/…
Даниил
Отличный ответ! Спасибо.
JLev
@ Грегор: Я думаю, чтобы подтвердить это, попробуйте onesвместо этого zeros, это гарантирует, что память фактически выделяется во время вызова соответствующей функции.
Даниил
Когда я все правильно понимаю, вывод такой: временной копии нет. Исключения из-за нехватки памяти возникают из-за того, что main_matназначены ненулевые значения Ранее была назначена только виртуальная память (адресное пространство), теперь она назначена физической памяти.
Даниил
1

Удаление parfor, скорее всего, решит вашу проблему.

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

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

См. «Решите, когда использовать parfor» в документации MATLAB, чтобы узнать больше о том, parforи когда использовать.

Крис Луенго
источник
1
Является ли удаление parfor является единственным способом ? Обработка работает лучше всего, когда я спроектировал ее таким образом, поскольку все внутри parforинтенсивно использует процессор и графический процессор, что значительно улучшило производительность.
Грегор Исак
@GregorIsack: Я пошел с вашим примером кода, не знал, что вы на самом деле много работали внутри parfor. Если так, то да, это, вероятно, полезно. - Может быть, если slice_matrixэто не так, gpuarrayон не будет скопирован в задании.
Крис
Хммм, даже если slice_matrixэто не так gpuArray, я все еще получаю симптом переполнения. Я оставлю этот вопрос открытым, посмотрим, есть ли альтернативное решение. Спасибо за ответ, хотя!
Грегор Исак
0

Я предполагаю, что ваш код - это просто пример кода, который rand()представляет собой обычай в вашем MVE. Итак, есть несколько советов и приемов для использования памяти в Matlab.

Вот фрагмент учебного пособия The MathWorks:

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

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

  • делают использование Натив векторизации MATLAB, например sum(X,2), mean(X,2),std(X,[],2)
  • убедитесь, что matlab не должен расширять матрицы ( неявное расширение было изменено недавно). Возможно, было бы более эффективно использоватьbsxfun
  • использовать операции на месте, например, x = 2*x+3вместоx = 2*x+3
  • ...

Имейте в виду, что оптимальное использование памяти не такое, как если бы вы хотели сократить время вычислений. Поэтому вы можете рассмотреть возможность сокращения числа работников или воздержаться от использования parfor-loop. (Поскольку parforнельзя использовать общую память, нет функции копирования при записи с использованием Parallel Toolbox.

Если вы хотите поближе взглянуть на свою память , на то, что доступно и что может использовать Matlab, ознакомьтесь feature('memstats'). Что интересно , для вас это виртуальная память , которая является

Общая и доступная память, связанная со всем процессом MATLAB. Он ограничен архитектурой процессора и операционной системой. или используйте эту команду [user,sys] = memory.

Быстрый побочный узел : Matlab последовательно сохраняет матрицы в памяти. Вам нужно иметь большой блок свободной оперативной памяти для больших матриц. Это также причина, по которой вы хотите выделить переменные, потому что их динамическое изменение заставляет Matlab копировать всю матрицу в большее место в ОЗУ каждый раз, когда оно перерастает текущее место.

Если у вас действительно есть проблемы с памятью , вы можете просто погрузиться в искусство типов данных - как требуется для языков более низкого уровня. Например, вы можете сократить использование памяти вдвое, используя одинарную точность непосредственно с самого начала main_mat=zeros(500,500,2000,'single');- кстати, это также работает с rand(...,'single')и более встроенными функциями - хотя некоторые из более сложных функций matlab требуют ввода типа double, который вы можете снова сбитый с толку.

Максимум
источник
0

Если я правильно понимаю, ваша главная проблема в том, что parforне позволяет делиться памятью. Думайте о каждом работнике parfor как об отдельном экземпляре matlab.

Мне известен только один обходной путь (который я никогда не пробовал), это «общая матрица» в Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

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

user2305193
источник
0

Вы можете использовать следующий код. Вам на самом деле не нужен slice_matrix

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
   parfor i=1:n
       main_mat(:,:,1+(k-1)*n + i - 1) = gather(gpuArray(rand(500,500)));
   end
   %% now you don't need this main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end
mayank1513
источник
Вы не можете сделать это внутри цикла parfor
Грегор Исак
Вы пробовали это?
mayank1513
Есть причина, почему я переместил это из цикла parfoor. Я не пробовал точно такой же код, но знал, что он не сработает из-за индексации.
Грегор Исак