Apa cara paling efisien untuk menulis loop 'for' di Matlab?

12

Saya telah membaca bahwa jika, misalnya, saya memiliki forloop ganda yang berjalan di atas indeks matriks, maka menempatkan indeks berjalan kolom di loop luar lebih efisien. Sebagai contoh:

a=zeros(1000);
for j=1:1000
 for i=1:1000
  a(i,j)=1;
 end
end

Apa cara paling efisien untuk mengkodekannya jika saya memiliki tiga forloop atau lebih ?

Sebagai contoh:

a=zeros(100,100,100);
for j=1:100
 for i=1:100
  for k=1:100
   a(i,j,k)=1;
  end
 end
end
Puluhan
sumber
4
Forloop sangat lambat di MATLAB. Anda harus menghindari loop eksplisit di MATLAB bila memungkinkan. Sebaliknya, biasanya masalah dapat dinyatakan dalam operasi matriks / vektor. Itu adalah cara MATLABic. Ada juga banyak fungsi bawaan untuk menginisialisasi matriks, dll. Misalnya, ada fungsi, ones () , yang akan mengatur semua elemen matriks ke 1 (dengan ekstensi, ke nilai apa pun dengan perkalian (skalar) dikalikan dengan semua-yang matriks)). Ini juga berfungsi pada array 3-D (yang menurut saya mencakup contoh di sini).
Peter Mortensen
3
@PeterMortensen Dengan faktor apa (kira-kira) efisiensi loop di Matlab lebih kecil dibandingkan dengan C dan Python? Dan mengapa begitu? Juga, bukankah efisiensi loop di Matlab menjadi lebih baik dalam beberapa tahun terakhir?
Puluhan
3
@PeterMortensen "biasanya masalah dapat diekspresikan dalam hal operasi matriks / vektor" - untuk nilai tertentu "biasanya", ya. IMO itu lebih akurat untuk mengatakan bahwa orang yang bekerja di Matlab dan sejenisnya memiliki budaya puluhan tahun mengabaikan semua hal yang tidak dapat dilakukan dengan operasi matriks / vektor, sehingga semuanya terlihat seperti paku bagi mereka untuk palu itu . Dan kita seharusnya tidak hanya mengatakan "untuk loop di Matlab lambat" tetapi "Matlab lambat" (hanya terkait dengan perpustakaan cepat primitif LA yang ditulis dalam C dan Fortran).
leftaroundabout
5
Performa untuk loop kontroversial: matlabtips.com/matlab-is-no-longer-slow-at-for-loops
ohreally
@leftaroundtentang Benar. Khawatir tentang kecepatan dalam bahasa yang ditafsirkan (atau semi-ditafsirkan) adalah indikasi yang cukup jelas Anda memiliki masalah XY di mana solusi sebenarnya adalah "jangan gunakan bahasa ini". Pengecualian tentu saja adalah jika Anda menggunakan pembuatan kode di Simulink, tetapi kemudian pertanyaannya adalah apa yang dihasilkan oleh pembuat kode C dan seberapa efisien itu.
Graham

Jawaban:

18

Jawaban singkat, Anda ingin memiliki indeks paling kiri di loop paling dalam. Dalam contoh Anda, indeks loop akan menjadi k, j, i dan indeks array adalah i, j, k. Ini ada hubungannya dengan bagaimana MATLAB menyimpan dimensi yang berbeda dalam memori. Untuk lebih lanjut, lihat # 13 dari posting reddit ini .

whpowell96
sumber
2
Atau gunakan fungsi fungsi bawaan () .
Peter Mortensen
5
Contoh @Peter OP hampir pasti hanya contoh mainan dari for for loop yang melakukan sesuatu dan bukan pada use case yang sebenarnya.
Matt
@ Mat Anda benar.
Puluhan
11

Jawaban yang agak lebih panjang yang menjelaskan mengapa lebih kiri memiliki indeks paling kiri bervariasi paling cepat. Ada dua hal utama yang perlu Anda pahami.

Pertama, MATLAB (dan Fortran, tetapi tidak C dan sebagian besar bahasa pemrograman lainnya) menyimpan array dalam memori dalam "urutan utama kolom." misalnya jika A adalah matriks 2 kali 3 kali 10, maka entri akan disimpan dalam memori dalam urutan

A (1,1,1)

A (2,1,1)

A (1,2,1)

A (2,2,1)

A (1,3,1)

A (2,3,1)

A (1,1,2)

A (2,1,2)

...

A (2,3,10)

Pilihan urutan utama kolom ini sewenang-wenang - kita dapat dengan mudah mengadopsi konvensi "urutan utama", dan pada kenyataannya itulah yang dilakukan dalam C dan beberapa bahasa pemrograman lainnya.

Hal penting kedua yang perlu Anda pahami adalah bahwa prosesor modern tidak mengakses memori satu lokasi pada suatu waktu, melainkan memuat dan menyimpan "garis cache" dari 64 atau bahkan 128 byte yang berdekatan (8 atau 16 angka floating point presisi ganda) pada suatu waktu dari memori. Potongan data ini disimpan sementara dalam cache memori cepat dan ditulis kembali sesuai kebutuhan. (Dalam praktiknya arsitektur cache sekarang cukup rumit dengan sebanyak 3 atau 4 level memori cache, tetapi ide dasarnya dapat dijelaskan dengan cache satu tingkat dari jenis yang dimiliki komputer di masa muda saya.)

A

Jika loop bersarang sehingga loop paling dalam memperbarui subscript baris, maka entri array akan diakses dalam urutan A (1,1), A (2,1), A (3,1), ... Ketika entri pertama A (1,1) diakses, sistem akan membawa garis cache yang mengandung A (1,1), A (2,1), ..., A (8,1) ke dalam cache dari memori utama . 8 iterasi berikutnya dari loop terdalam bekerja pada data ini tanpa transfer memori utama tambahan.

Jika dalam alternatif, kita menyusun loop sehingga indeks kolom bervariasi dalam loop paling dalam, maka entri A akan diakses dalam urutan A (1,1), A (1,2), A (1,3 ), ... Dalam hal ini, akses pertama akan membawa A (1,1), A (2,1), ..., A (8,1) ke dalam cache dari memori utama, tetapi 7/8 dari entri ini tidak akan digunakan. Akses ke A (1,2) dalam iterasi kedua kemudian akan membawa 8 entri lainnya dari memori utama, dan seterusnya. Pada saat kode mulai bekerja pada baris 2 dari matriks, entri A (2,1) mungkin dihapus dari cache untuk memberi jalan bagi data lain yang diperlukan. Akibatnya, kode ini menghasilkan lalu lintas 8 kali lebih banyak dari yang diperlukan.

Beberapa kompiler yang mengoptimalkan mampu merestrukturisasi loop secara otomatis untuk menghindari masalah ini.

Banyak algoritma aljabar linear numerik untuk perkalian dan faktorisasi matriks dapat dioptimalkan untuk bekerja secara efisien dengan skema pengurutan baris-mayor atau kolom-mayor tergantung pada bahasa pemrograman. Melakukan ini dengan cara yang salah dapat memiliki dampak negatif yang signifikan terhadap kinerja.

Brian Borchers
sumber