Anda bisa mendapatkan ide dengan menjalankan versi lain dari kode Anda. Pertimbangkan untuk menulis perhitungan secara eksplisit, daripada menggunakan fungsi dalam loop Anda
tic
Soln3 = ones(T, N);
for t = 1:T
for n = 1:N
Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Waktu untuk menghitung di komputer saya:
Soln1 1.158446 seconds.
Soln2 10.392475 seconds.
Soln3 0.239023 seconds.
Oli 0.010672 seconds.
Sekarang, meskipun solusi 'vektorisasi' sepenuhnya jelas merupakan yang tercepat, Anda dapat melihat bahwa menentukan fungsi yang akan dipanggil untuk setiap entri x adalah biaya tambahan yang sangat besar . Hanya secara eksplisit menulis perhitungan memberi kami faktor kecepatan 5. Saya rasa ini menunjukkan bahwa MATLABs JIT compiler tidak mendukung fungsi inline . Menurut jawaban gnovice di sana, sebenarnya lebih baik menulis fungsi normal daripada yang anonim. Cobalah.
Langkah selanjutnya - hapus (vektorisasi) loop dalam:
tic
Soln4 = ones(T, N);
for t = 1:T
Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc
Soln4 0.053926 seconds.
Faktor lain 5 percepatan: ada sesuatu dalam pernyataan itu yang mengatakan Anda harus menghindari loop di MATLAB ... Atau benarkah? Lihat ini kalau begitu
tic
Soln5 = ones(T, N);
for n = 1:N
Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc
Soln5 0.013875 seconds.
Lebih dekat dengan versi vektorisasi 'sepenuhnya'. Matlab menyimpan matriks berdasarkan kolom. Anda harus selalu (jika mungkin) menyusun komputasi Anda menjadi 'kolom-bijaksana' vektor.
Kita bisa kembali ke Soln3 sekarang. Urutan loop ada 'baris-bijaksana'. Mari kita ubah
tic
Soln6 = ones(T, N);
for n = 1:N
for t = 1:T
Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Soln6 0.201661 seconds.
Lebih baik, tapi tetap sangat buruk. Putaran tunggal - bagus. Putaran ganda - buruk. Saya kira MATLAB melakukan beberapa pekerjaan yang layak untuk meningkatkan kinerja loop, tetapi overhead loop masih ada. Jika Anda memiliki pekerjaan yang lebih berat di dalam, Anda tidak akan menyadarinya. Tetapi karena komputasi ini dibatasi bandwidth memori, Anda akan melihat overhead loop. Dan Anda bahkan akan lebih jelas melihat overhead pemanggilan Func1 di sana.
Jadi ada apa dengan arrayfun? Tidak ada fungsi inlinig di sana, jadi banyak overhead. Tetapi mengapa jauh lebih buruk daripada loop bersarang ganda? Sebenarnya topik penggunaan cellfun / arrayfun sudah banyak dibahas berkali-kali (misalnya disini , disini , disini dan disini ). Fungsi-fungsi ini lambat sekali, Anda tidak dapat menggunakannya untuk perhitungan halus seperti itu. Anda dapat menggunakannya untuk singkatnya kode dan konversi mewah antara sel dan array. Tetapi fungsinya harus lebih berat dari yang Anda tulis:
tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc
Soln7 0.016786 seconds.
Perhatikan bahwa Soln7 sekarang adalah sel .. terkadang itu berguna. Kinerja kode cukup baik sekarang, dan jika Anda membutuhkan sel sebagai keluaran, Anda tidak perlu mengonversi matriks Anda setelah Anda menggunakan solusi vektor penuh.
Jadi mengapa arrayfun lebih lambat dari struktur loop sederhana? Sayangnya, kami tidak dapat mengatakan dengan pasti, karena tidak ada kode sumber yang tersedia. Anda hanya dapat menebaknya karena arrayfun adalah fungsi bertujuan umum, yang menangani semua jenis struktur data dan argumen yang berbeda, tidak harus sangat cepat dalam kasus sederhana, yang dapat Anda ekspresikan secara langsung sebagai sarang loop. Dari mana datangnya biaya overhead, kita tidak bisa tahu. Bisakah overhead dihindari dengan implementasi yang lebih baik? Mungkin tidak. Tapi sayangnya satu-satunya hal yang dapat kita lakukan adalah mempelajari kinerja untuk mengidentifikasi kasus-kasus, di mana itu bekerja dengan baik, dan di mana tidak.
Perbarui Karena waktu pelaksanaan tes ini singkat, untuk mendapatkan hasil yang andal, saya menambahkan sekarang putaran di sekitar tes:
for i=1:1000
% compute
end
Beberapa waktu yang diberikan di bawah ini:
Soln5 8.192912 seconds.
Soln7 13.419675 seconds.
Oli 8.089113 seconds.
Anda melihat bahwa arrayfun masih buruk, tetapi setidaknya tidak tiga kali lipat lebih buruk daripada solusi vektor. Di sisi lain, satu loop dengan perhitungan berdasarkan kolom secepat versi vektor penuh ... Itu semua dilakukan pada satu CPU. Hasil untuk Soln5 dan Soln7 tidak berubah jika saya beralih ke 2 core - Dalam Soln5 saya harus menggunakan parfor untuk membuatnya diparalelkan. Lupakan tentang speedup ... Soln7 tidak berjalan secara paralel karena arrayfun tidak berjalan secara paralel. Versi vektor Olis di sisi lain:
Oli 5.508085 seconds.