Bagaimana cara 'menyalin' matriks tanpa membuat matriks sementara dalam memori yang menyebabkan memori meluap?

9

Dengan menetapkan matriks ke dalam memori yang dialokasikan jauh lebih besar, matlab entah bagaimana akan menduplikatnya sambil 'menyalinnya, dan jika matriks yang akan disalin cukup besar, akan ada memori yang berlebihan. Ini adalah kode contoh:

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

Adakah cara untuk hanya 'menghancurkan' slice_matrixke atas main_mattanpa overhead? Terima kasih sebelumnya.

EDIT:

Overflow terjadi ketika main_matdialokasikan sebelumnya. Jika main_matdiinisialisasi dengan main_mat=zeros(500,500,1);(ukuran lebih kecil), overflow tidak akan terjadi, tetapi akan melambat karena alokasi tidak dilakukan sebelum matriks ditugaskan ke dalamnya. Ini akan secara signifikan mengurangi kinerja karena kisaran kkenaikan

Gregor Isack
sumber
1
Mengenai loop Anda: disarankan untuk mengatur loop luar ke parforloop untuk tujuan optimasi . Selain itu, parforsalin data Anda ke setiap pekerja yang terpisah, sehingga dengan asumsi 4 pekerja itu menggandakan data Anda empat kali dalam RAM.
Adriaan
1
Apa indikasi Anda bahwa Matlab sebenarnya menggandakan memori? Apakah Anda menggunakan memoryfungsi ini? Manajer tugas? Kesalahan memori dari Matlab? Pada baris kode apa itu terjadi?
Eliahu Aaron
Seperti yang Anda lihat di mana saya mengomentari kode, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)adalah di mana masalah memori meluap terjadi. Itu diverifikasi ketika saya mengalokasikan main_matsebelumnya, itu akan meluap, jika saya tidak, itu tidak akan. Matlab akan mengembalikan 'kehabisan memori kesalahan'.
Gregor Isack
Apakah matriks 500x500x2000 Anda sesuai dengan memori? Ini ~ 4 Gb. Lihat stackoverflow.com/q/51987892/7328782 untuk mengapa kesalahan kehabisan memori hanya dapat terjadi saat menulis ke array.
Cris Luengo
Untuk lebih memahami masalah Anda, dapatkah Anda menyisipkan h=h+slice_matrix(end)sebelum main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(dan menginisialisasi h dengan 0)? Saya menduga bahwa baris yang baru ditambahkan ini sudah akan menyebabkan masalah memori Anda.
Daniel

Jawaban:

4

Masalah utama adalah bahwa angka membutuhkan lebih banyak ruang daripada nol. main_mat=zeros(500,500,2000);membutuhkan sedikit RAM saat main_mat = rand(500,500,2000);mengambil banyak, tidak masalah jika Anda menggunakan GPU atau parfor (pada kenyataannya, parfor akan membuat Anda menggunakan lebih banyak RAM). Jadi ini bukan pembengkakan memori yang tidak wajar. Mengikuti tautan Daniel di bawah ini, tampaknya penugasan nol hanya menciptakan pointer ke memori, dan memori fisik hanya diisi ketika Anda menggunakan matriks untuk "angka". Ini dikelola oleh sistem operasi. Dan itu diharapkan untuk Windows, Mac dan Linux, baik Anda melakukannya dengan Matlab atau bahasa lain seperti C.

Yuval Harpaz
sumber
Saat ini saya tidak lagi mengerti MATLAB. Setelah saya mengetikkan perintah dengan zerosseluruh memori virtual sebenarnya dialokasikan, tetapi tidak ada memori yang digunakan. whosmenunjukkan ukuran yang sama untuk kedua matriks, sedangkan os saya menunjukkan konsumsi memori yang berbeda. Saya menghapus komentar saya karena jawaban Anda pasti tidak salah.
Daniel
3
Saya menemukan sesuatu yang menjelaskan hal ini: stackoverflow.com/questions/51987892/…
Daniel
Jawaban bagus! Terima kasih.
JLev
@ Gregor: Saya kira untuk mengkonfirmasi ini, cobalah dengan onesalih - alih zeros, ini memastikan memori sebenarnya dialokasikan pada saat memanggil fungsi masing-masing.
Daniel
Ketika saya mengerti semuanya dengan benar, kesimpulannya adalah: Tidak ada salinan sementara. Pengecualian kehabisan memori muncul karena main_matdiberikan nilai bukan nol. Sebelumnya hanya memori virtual (ruang alamat) yang ditetapkan, ini sekarang ditugaskan untuk memori fisik.
Daniel
1

Menghapus parfor kemungkinan akan memperbaiki masalah Anda.

parfortidak berguna disana. MATLABparfor tidak menggunakan paralelisme memori bersama (yaitu tidak memulai utas baru) melainkan paralelisme memori terdistribusi (ini memulai proses baru). Ini dirancang untuk mendistribusikan pekerjaan melalui satu set atau node pekerja. Dan meskipun ia juga bekerja dalam satu node (atau satu komputer desktop) untuk mendistribusikan pekerjaan melalui beberapa core, itu bukan cara optimal untuk melakukan paralelisme dalam satu node.

Ini berarti bahwa setiap proses yang dimulai parforharus memiliki salinannya sendirislice_matrix , yang merupakan penyebab besarnya jumlah memori yang digunakan oleh program Anda.

Lihat "Putuskan Kapan Menggunakan parfor" dalam dokumentasi MATLAB untuk mempelajari lebih lanjut tentang parfordan kapan menggunakannya.

Cris Luengo
sumber
1
Apakah menghapus parfor adalah satu-satunya cara ? Pemrosesan bekerja paling baik ketika saya mendesainnya, karena semua yang ada di dalamnya parforadalah CPU dan GPU intensif, sehingga secara signifikan meningkatkan kinerja.
Gregor Isack
@GregorIsack: Saya menggunakan kode contoh Anda, tidak tahu Anda benar-benar melakukan banyak pekerjaan di dalamnya parfor. Jika demikian, maka ya, itu kemungkinan berguna. - Mungkin jika slice_matrixbukan gpuarrayitu tidak akan disalin dalam tugas.
Cris Luengo
Hmmm walaupun slice_matrixbukan gpuArray, saya masih mendapatkan gejala overflow. Saya akan membiarkan pertanyaan ini terbuka, mari kita lihat apakah ada solusi alternatif. Terima kasih atas jawabannya!
Gregor Isack
0

Saya berasumsi bahwa kode Anda hanya kode sampel dan itu rand() mewakili kebiasaan di MVE Anda. Jadi ada beberapa petunjuk dan trik untuk penggunaan memori di matlab.

Ada cuplikan dari buku pegangan pelatihan The MathWorks:

Ketika menugaskan satu variabel ke yang lain di MATLAB, seperti yang terjadi ketika melewati parameter ke suatu fungsi, MATLAB secara transparan membuat referensi ke variabel itu. MATLAB memecah referensi, dan membuat salinan variabel itu, hanya ketika kode memodifikasi satu atau lebih nilai. Perilaku ini, yang dikenal sebagai copy-on-write , atau lazy-copying , menghalangi biaya menyalin set data besar sampai kode memodifikasi nilai. Oleh karena itu, jika kode tidak melakukan modifikasi, tidak perlu ruang memori tambahan dan waktu eksekusi untuk menyalin variabel.

Hal pertama yang harus dilakukan adalah memeriksa efisiensi (memori) kode Anda. Bahkan kode programer yang sangat baik dapat lebih dioptimalkan dengan (sedikit) kekuatan otak. Berikut adalah beberapa petunjuk mengenai efisiensi memori

  • make penggunaan vektorisasi nativ dari Matlab, misalnya sum(X,2), mean(X,2),std(X,[],2)
  • pastikan bahwa matlab tidak perlu memperluas matriks ( perluasan implisit telah diubah baru-baru ini). Mungkin lebih efisien untuk menggunakanbsxfun
  • gunakan operasi di tempat, mis. x = 2*x+3alih-alihx = 2*x+3
  • ...

Ketahuilah bahwa penggunaan memori yang optimal tidak sama dengan jika Anda ingin mengurangi waktu komputasi. Karena itu, Anda mungkin ingin mempertimbangkan mengurangi jumlah pekerja atau menahan diri untuk tidak menggunakan parfor-loop. (Karena parfortidak dapat menggunakan memori bersama, tidak ada fitur copy-on-write dengan menggunakan Toolbox Paralel.

Jika Anda ingin melihat lebih dekat pada memori Anda , apa yang tersedia dan yang dapat digunakan oleh Matlab, periksa feature('memstats'). Yang menarik bagi Anda adalah Memori Virtual itu

Memori total dan tersedia yang terkait dengan keseluruhan proses MATLAB. Itu dibatasi oleh arsitektur prosesor dan sistem operasi. atau gunakan perintah ini [user,sys] = memory.

Node sisi cepat : Matlab menyimpan matriks secara konsisten dalam memori. Anda perlu memiliki blok besar RAM gratis untuk matriks besar. Itu juga alasan mengapa Anda ingin mengalokasikan variabel, karena mengubahnya secara dinamis memaksa Matlab untuk menyalin seluruh matriks ke tempat yang lebih besar di RAM setiap kali melebihi tempat saat ini.

Jika Anda benar-benar memiliki masalah memori , Anda mungkin hanya ingin menggali seni tipe data - seperti yang diperlukan dalam bahasa tingkat yang lebih rendah. Misalnya Anda dapat memotong penggunaan memori Anda menjadi dua dengan menggunakan presisi tunggal langsung dari awal main_mat=zeros(500,500,2000,'single');- btw, ini juga berfungsi dengan rand(...,'single')dan lebih banyak fungsi asli - meskipun beberapa fungsi matlab yang lebih canggih membutuhkan input tipe ganda, yang Anda dapat disadap lagi.

maks
sumber
0

Jika saya mengerti benar masalah utama Anda adalah parfortidak memungkinkan untuk berbagi memori. Pikirkan setiap pekerja parfor sebagai contoh matlab yang terpisah.

Pada dasarnya hanya ada satu solusi untuk ini yang saya tahu (yang belum pernah saya coba), yaitu 'shared matrix' di Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Lebih banyak solusi: seperti yang disarankan orang lain: menghapus parfor tentu saja merupakan salah satu solusi, mendapatkan lebih banyak ram, menggunakan array tinggi (yang menggunakan harddrives ketika ram berjalan penuh, baca di sini ), membagi operasi dalam potongan-potongan yang lebih kecil, last but not least, pertimbangkan alternatif selain Matlab.

pengguna2305193
sumber
0

Anda dapat menggunakan kode berikut. Anda sebenarnya tidak membutuhkan 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
sumber
Anda tidak dapat melakukannya di dalam lingkaran
parfor
Apakah kamu mencobanya?
mayank1513
Ada alasan mengapa saya memindahkannya dari parfoor loop. Saya tidak mencoba kode yang sama persis, tetapi saya tahu itu tidak akan berhasil karena pengindeksan.
Gregor Isack