Як "скопіювати" матрицю без створення тимчасової матриці в пам'яті, яка спричинила переповнення пам'яті?


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 Gb. Дивіться 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);, забираючи багато, незалежно від того, використовуєте ви GPU чи parfor (адже, parfor змусить вас використовувати більше оперативної пам’яті). Так що це не протиприродний набряк пам’яті. Перейшовши за посиланням Данила нижче, здається, що призначення нулів створює лише покажчики на пам'ять, а фізична пам'ять заповнюється лише тоді, коли ви використовуєте матрицю для "чисел". Цим керує операційна система. І це очікується для Windows, Mac та Linux, або ви робите це з Matlab або іншими мовами, такими як C.


Зараз я вже не розумію MATLAB. Як тільки я набираю команди з zerosусією віртуальною пам'яттю, фактично виділяється, але пам'ять не використовується. whosпоказує однаковий розмір для обох матриць, в той час як мій os показує різне споживання пам'яті. Я видалив свій коментар, тому що ваша відповідь, безумовно, не помиляється.
Даниїл

3
Я знайшов щось, що пояснює це: stackoverflow.com/questions/51987892/…
Даниїл

Чудова відповідь! Дякую.
JLev

@Gregor: Я думаю, щоб підтвердити це, спробуйте це 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');- btw, це також працює rand(...,'single')і з більш рідними функціями - хоча для деяких більш досконалих функцій matlab потрібен введення подвійного типу, який ви можете знову змушений.


0

Якщо я правильно розумію, ваша основна проблема полягає в тому, що parforце не дозволяє ділитися пам’яттю. Розгляньте кожного парфопрацівника як майже окремий екземпляр матлабу.

Для цього є лише один спосіб вирішення цього питання, який я знаю (що я ніколи не пробував), тобто "спільна матриця" у Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Більше рішень: як пропонують інші: видалити парфор, безумовно, одне рішення, отримати більше оперативної пам’яті, використовувати високі масиви (які використовують жорсткі диски, коли таран працює повністю, читайте тут ), діліть операції на менші шматки, в останню чергу, розглядайте альтернативу, крім Матлаб.


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

Є причина, чому я перемістив це з петлі для підлоги. Я не пробував абсолютно той самий код, але знав, що він не працюватиме через індексацію.
Грегор Ісак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.