Pertanyaan semacam ini berulang dan harus dijawab lebih jelas daripada "MATLAB menggunakan perpustakaan yang sangat dioptimalkan" atau "MATLAB menggunakan MKL" untuk sekali di Stack Overflow.
Sejarah:
Perkalian matriks (bersama-sama dengan matriks-vektor, perkalian vektor-vektor dan banyak dari penguraian matriks) adalah masalah yang paling penting dalam aljabar linier. Insinyur telah memecahkan masalah ini dengan komputer sejak awal.
Saya bukan ahli dalam sejarah, tetapi tampaknya saat itu, semua orang hanya menulis ulang versi FORTRAN-nya dengan loop sederhana. Beberapa standardisasi kemudian muncul, dengan identifikasi "kernel" (rutinitas dasar) yang paling dibutuhkan masalah aljabar linier agar dapat dipecahkan. Operasi dasar ini kemudian distandarisasi dalam spesifikasi yang disebut: Basic Linear Aljabar Subprograms (BLAS). Insinyur kemudian dapat memanggil rutin BLAS standar ini yang telah teruji baik dalam kode mereka, membuat pekerjaan mereka jauh lebih mudah.
BLAS:
BLAS berevolusi dari level 1 (versi pertama yang mendefinisikan operasi skalar-vektor dan vektor-vektor) ke level 2 (operasi matriks-vektor) ke level 3 (operasi matriks-matriks), dan semakin banyak menyediakan "kernel" sehingga standar dan lebih banyak operasi aljabar linier mendasar. Implementasi FORTRAN 77 asli masih tersedia di situs web Netlib .
Menuju kinerja yang lebih baik:
Jadi selama bertahun-tahun (terutama antara rilis BLAS level 1 dan level 2: awal 80-an), perangkat keras berubah, dengan munculnya operasi vektor dan hierarki cache. Evolusi ini memungkinkan untuk meningkatkan kinerja subrutin BLAS secara substansial. Vendor yang berbeda kemudian datang dengan implementasi rutinitas BLAS mereka yang semakin efisien.
Saya tidak tahu semua implementasi historis (saya tidak dilahirkan atau masih kecil saat itu), tetapi dua yang paling menonjol keluar pada awal 2000-an: Intel MKL dan GotoBLAS. Matlab Anda menggunakan Intel MKL, yang merupakan BLAS yang sangat bagus, dioptimalkan, dan itu menjelaskan kinerja hebat yang Anda lihat.
Rincian teknis tentang perkalian Matriks:
Jadi mengapa Matlab (MKL) begitu cepat dgemm
(multiplikasi matriks-matriks umum presisi ganda) Secara sederhana: karena menggunakan vektorisasi dan caching data yang baik. Dalam istilah yang lebih kompleks: lihat artikel yang disediakan oleh Jonathan Moore.
Pada dasarnya, ketika Anda melakukan perkalian dalam kode C ++ yang Anda berikan, Anda sama sekali tidak ramah terhadap cache. Karena saya curiga Anda membuat array pointer ke array baris, akses Anda di loop batin Anda ke kolom k-th "matice2": matice2[m][k]
sangat lambat. Memang, ketika Anda mengakses matice2[0][k]
, Anda harus mendapatkan elemen k-th dari array 0 dari matriks Anda. Kemudian dalam iterasi berikutnya, Anda harus mengakses matice2[1][k]
, yang merupakan elemen k-th dari array lain (array 1). Kemudian di iterasi berikutnya Anda mengakses array lain, dan seterusnya ... Karena seluruh matriks matice2
tidak dapat ditampung di cache tertinggi (ini 8*1024*1024
byte besar), program harus mengambil elemen yang diinginkan dari memori utama, kehilangan banyak waktu.
Jika Anda baru saja memindahkan matriks, sehingga akses akan berada di alamat memori yang berdekatan, kode Anda akan berjalan jauh lebih cepat karena sekarang kompiler dapat memuat seluruh baris dalam cache pada saat yang sama. Coba saja versi modifikasi ini:
timer.start();
float temp = 0;
//transpose matice2
for (int p = 0; p < rozmer; p++)
{
for (int q = 0; q < rozmer; q++)
{
tempmat[p][q] = matice2[q][p];
}
}
for(int j = 0; j < rozmer; j++)
{
for (int k = 0; k < rozmer; k++)
{
temp = 0;
for (int m = 0; m < rozmer; m++)
{
temp = temp + matice1[j][m] * tempmat[k][m];
}
matice3[j][k] = temp;
}
}
timer.stop();
Jadi Anda dapat melihat bagaimana hanya cache lokalitas meningkatkan kinerja kode Anda secara substansial. Sekarang dgemm
implementasi nyata mengeksploitasi itu ke tingkat yang sangat luas: Mereka melakukan perkalian pada blok dari matriks yang didefinisikan oleh ukuran TLB (buffer lookaside terjemahan, cerita panjang pendek: apa yang secara efektif dapat di-cache), sehingga mereka mengalir ke prosesor persis jumlah data yang dapat diproses. Aspek lainnya adalah vektorisasi, mereka menggunakan instruksi yang di-vektor-kan prosesor untuk throughput instruksi yang optimal, yang tidak dapat Anda lakukan dari kode C ++ cross-platform Anda.
Akhirnya, orang mengklaim bahwa itu karena algoritma Strassen atau Coppersmith-Winograd salah, kedua algoritma ini tidak dapat diterapkan dalam praktik, karena pertimbangan perangkat keras yang disebutkan di atas.