Bagaimana BLAS mendapatkan performa ekstrim seperti itu?

108

Karena penasaran saya memutuskan untuk mengukur fungsi perkalian matriks saya sendiri versus implementasi BLAS ... Saya harus mengatakan yang paling tidak terkejut dengan hasilnya:

Implementasi Kustom, 10 uji coba perkalian matriks 1000x1000:

Took: 15.76542 seconds.

Implementasi BLAS, 10 percobaan perkalian matriks 1000x1000:

Took: 1.32432 seconds.

Ini menggunakan angka floating point presisi tunggal.

Implementasi Saya:

template<class ValT>
void mmult(const ValT* A, int ADim1, int ADim2, const ValT* B, int BDim1, int BDim2, ValT* C)
{
    if ( ADim2!=BDim1 )
        throw std::runtime_error("Error sizes off");

    memset((void*)C,0,sizeof(ValT)*ADim1*BDim2);
    int cc2,cc1,cr1;
    for ( cc2=0 ; cc2<BDim2 ; ++cc2 )
        for ( cc1=0 ; cc1<ADim2 ; ++cc1 )
            for ( cr1=0 ; cr1<ADim1 ; ++cr1 )
                C[cc2*ADim2+cr1] += A[cc1*ADim1+cr1]*B[cc2*BDim1+cc1];
}

Saya punya dua pertanyaan:

  1. Diketahui bahwa perkalian matriks-matriks mengatakan: nxm * mxn memerlukan perkalian n * n * m, jadi dalam kasus operasi di atas 1000 ^ 3 atau 1e9. Bagaimana mungkin pada prosesor 2.6Ghz saya untuk BLAS melakukan operasi 10 * 1e9 dalam 1,32 detik? Bahkan jika perkalian adalah satu operasi dan tidak ada lagi yang dilakukan, itu akan memakan waktu ~ 4 detik.
  2. Mengapa penerapan saya jauh lebih lambat?
DeusAduro
sumber
17
BLAS telah dioptimalkan di satu sisi dan di sisi lain oleh spesialis di lapangan. Saya berasumsi itu memanfaatkan unit titik mengambang SIMD pada chip Anda dan memainkan banyak trik untuk meningkatkan perilaku caching juga ...
dmckee --- mantan moderator kucing
3
Masih bagaimana Anda melakukan operasi 1E10 pada prosesor 2.63E9 siklus / detik dalam 1,3 detik?
DeusAduro
9
Multiple execution unit, pipe-lining, dan Single Instruction Multiple Data ((SIMD) yang berarti melakukan operasi yang sama pada lebih dari satu pasang operan pada waktu yang sama). Beberapa kompiler dapat menargetkan unit SIMD pada chip umum tetapi Anda hampir selalu harus menyalakannya secara eksplisit, dan ini membantu untuk mengetahui bagaimana semuanya bekerja ( en.wikipedia.org/wiki/SIMD ). Mengasuransikan cache miss hampir pasti merupakan bagian yang sulit.
dmckee --- kucing mantan moderator
13
Anggapan itu salah. Ada algoritma yang lebih dikenal, lihat Wikipedia.
MSalters
2
@ DeusAduro: Dalam jawaban saya untuk Bagaimana cara menulis produk matriks matriks yang dapat bersaing dengan Eigen? Saya memposting contoh kecil tentang cara menerapkan produk matriks-matriks yang efisien cache.
Michael Lehn

Jawaban:

141

Titik awal yang baik adalah buku hebat The Science of Programming Matrix Computations oleh Robert A. van de Geijn dan Enrique S. Quintana-Ortí. Mereka menyediakan versi unduhan gratis.

BLAS dibagi menjadi tiga tingkatan:

  • Level 1 mendefinisikan sekumpulan fungsi aljabar linier yang beroperasi hanya pada vektor. Fungsi-fungsi ini mendapatkan keuntungan dari vektorisasi (misalnya dari penggunaan SSE).

  • Fungsi level 2 adalah operasi matriks-vektor, misalnya beberapa hasil kali matriks-vektor. Fungsi-fungsi ini dapat diimplementasikan dalam kaitannya dengan fungsi Level1. Namun, Anda dapat meningkatkan kinerja fungsi ini jika Anda dapat menyediakan implementasi khusus yang menggunakan beberapa arsitektur multiprosesor dengan memori bersama.

  • Fungsi level 3 adalah operasi seperti hasil kali matriks-matriks. Sekali lagi Anda bisa mengimplementasikannya dalam kaitannya dengan fungsi Level2. Tetapi fungsi Level3 melakukan operasi O (N ^ 3) pada data O (N ^ 2). Jadi, jika platform Anda memiliki hierarki cache, Anda dapat meningkatkan kinerja jika Anda menyediakan implementasi khusus yang dioptimalkan untuk cache / ramah cache . Ini dijelaskan dengan baik di buku. Peningkatan utama fungsi Level3 berasal dari pengoptimalan cache. Peningkatan ini secara signifikan melebihi dorongan kedua dari paralelisme dan pengoptimalan perangkat keras lainnya.

Ngomong-ngomong, sebagian besar (atau bahkan semua) implementasi BLAS berkinerja tinggi TIDAK diterapkan di Fortran. ATLAS diimplementasikan di C. GotoBLAS / OpenBLAS diimplementasikan di C dan kinerja bagian penting di Assembler. Hanya implementasi referensi BLAS yang dilaksanakan di Fortran. Namun, semua implementasi BLAS ini menyediakan antarmuka Fortran sehingga dapat ditautkan ke LAPACK (LAPACK mendapatkan semua kinerjanya dari BLAS).

Kompiler yang dioptimalkan memainkan peran kecil dalam hal ini (dan untuk GotoBLAS / OpenBLAS, kompiler tidak menjadi masalah sama sekali).

Implementasi IMHO no BLAS menggunakan algoritma seperti algoritma Coppersmith – Winograd atau algoritma Strassen. Saya tidak begitu yakin tentang alasannya, tapi ini tebakan saya:

  • Mungkin tidak mungkin untuk menyediakan implementasi cache yang dioptimalkan dari algoritma ini (yaitu Anda akan kehilangan lebih banyak daripada Anda akan menang)
  • Algoritme ini secara numerik tidak stabil. Karena BLAS adalah kernel komputasi LAPACK, ini tidak boleh dilakukan.

Edit / Perbarui:

Makalah baru dan terobosan untuk topik ini adalah makalah BLIS . Mereka ditulis dengan sangat baik. Untuk kuliah saya "Dasar-dasar Perangkat Lunak untuk Komputasi Kinerja Tinggi", saya mengimplementasikan produk matriks-matriks setelah makalah mereka. Sebenarnya saya menerapkan beberapa varian dari produk matriks-matriks. Varian paling sederhana seluruhnya ditulis dalam C biasa dan memiliki kurang dari 450 baris kode. Semua varian lainnya hanya mengoptimalkan loop

    for (l=0; l<MR*NR; ++l) {
        AB[l] = 0;
    }
    for (l=0; l<kc; ++l) {
        for (j=0; j<NR; ++j) {
            for (i=0; i<MR; ++i) {
                AB[i+j*MR] += A[i]*B[j];
            }
        }
        A += MR;
        B += NR;
    }

Performa keseluruhan dari produk matriks-matriks hanya bergantung pada loop ini. Sekitar 99,9% waktu dihabiskan di sini. Dalam varian lain saya menggunakan kode intrinsik dan assembler untuk meningkatkan kinerja. Anda dapat melihat tutorial melalui semua varian di sini:

ulmBLAS: Tutorial tentang GEMM (Matrix-Matrix Product)

Bersama dengan makalah BLIS, menjadi cukup mudah untuk memahami bagaimana perpustakaan seperti Intel MKL dapat memperoleh kinerja seperti itu. Dan mengapa tidak masalah apakah Anda menggunakan penyimpanan utama baris atau kolom!

Tolok ukur terakhir ada di sini (kami menyebut proyek kami ulmBLAS):

Tolok ukur untuk ulmBLAS, BLIS, MKL, openBLAS dan Eigen

Edit / Pembaruan Lain:

Saya juga menulis beberapa tutorial tentang bagaimana BLAS digunakan untuk masalah aljabar linier numerik seperti memecahkan sistem persamaan linier:

Faktorisasi LU Kinerja Tinggi

(Faktorisasi LU ini misalnya digunakan oleh Matlab untuk menyelesaikan sistem persamaan linier.)

Saya berharap dapat menemukan waktu untuk memperpanjang tutorial untuk menjelaskan dan mendemonstrasikan bagaimana mewujudkan implementasi paralel yang sangat skalabel dari faktorisasi LU seperti di PLASMA .

Oke, ini dia: Coding a Cache Optimized Parallel LU Factorization

PS: Saya juga melakukan beberapa percobaan untuk meningkatkan kinerja uBLAS. Sebenarnya cukup mudah untuk meningkatkan (ya, mainkan kata-kata :)) kinerja uBLAS:

Eksperimen di uBLAS .

Berikut proyek serupa dengan BLAZE :

Eksperimen di BLAZE .

Michael Lehn
sumber
3
Tautan baru ke “Benchmarks for ulmBLAS, BLIS, MKL, openBLAS and Eigen”: apfel.mathematik.uni-ulm.de/~lehn/ulmBLAS/#toc3
Ahmed Fasih
Ternyata ESSL IBM menggunakan variasi dari algoritma Strassen - ibm.com/support/knowledgecenter/en/SSFHY8/essl_welcome.html
ben-albrecht
2
sebagian besar tautan sudah mati
Aurélien Pierre
PDF TSoPMC dapat ditemukan di halaman penulis, di cs.utexas.edu/users/rvdg/tmp/TSoPMC.pdf
Alex Shpilkin
Meskipun algoritme Coppersmith-Winograd memiliki kompleksitas waktu yang bagus di atas kertas, notasi O Besar menyembunyikan konstanta yang sangat besar, sehingga hanya mulai dapat digunakan untuk matriks yang sangat besar.
DiehardTheTryhard
26

Jadi pertama-tama BLAS hanyalah antarmuka dari sekitar 50 fungsi. Ada banyak implementasi antarmuka yang bersaing.

Pertama saya akan menyebutkan hal-hal yang sebagian besar tidak terkait:

  • Fortran vs C, tidak ada bedanya
  • Algoritme matriks lanjutan seperti Strassen, implementasi tidak menggunakannya karena tidak membantu dalam praktik

Sebagian besar implementasi memecah setiap operasi menjadi matriks berdimensi kecil atau operasi vektor dengan cara yang lebih atau kurang jelas. Misalnya perkalian matriks besar 1000x1000 dapat dipecah menjadi urutan perkalian matriks 50x50.

Operasi dimensi kecil ukuran tetap ini (disebut kernel) di-hardcode dalam kode assembly khusus CPU menggunakan beberapa fitur CPU dari targetnya:

  • Instruksi model SIMD
  • Paralelisme Tingkat Instruksi
  • Cache-awareness

Selanjutnya kernel ini dapat dieksekusi secara paralel satu sama lain menggunakan beberapa utas (inti CPU), dalam pola desain pengurangan peta yang khas.

Lihatlah ATLAS yang merupakan implementasi BLAS open source yang paling umum digunakan. Ini memiliki banyak kernel yang bersaing, dan selama proses pembuatan pustaka ATLAS, ia menjalankan persaingan di antara mereka (beberapa bahkan berparameter, jadi kernel yang sama dapat memiliki pengaturan yang berbeda). Ia mencoba konfigurasi yang berbeda dan kemudian memilih yang terbaik untuk sistem target tertentu.

(Tip: Itulah sebabnya jika Anda menggunakan ATLAS, lebih baik Anda membuat dan menyetel library secara manual untuk mesin tertentu Anda, kemudian menggunakan yang sudah dibuat sebelumnya.)

Andrew Tomazos
sumber
ATLAS bukan lagi implementasi BLAS open source yang paling umum digunakan. Itu telah dilampaui oleh OpenBLAS (cabang dari GotoBLAS) dan BLIS (refactoring dari GotoBLAS).
Robert van de Geijn
1
@ ulaff.net: Itu mungkin. Ini ditulis 6 tahun yang lalu. Saya pikir implementasi BLAS tercepat saat ini (pada Intel tentu saja) adalah Intel MKL, tetapi ini bukan open source.
Andrew Tomazos
14

Pertama, ada algoritme yang lebih efisien untuk perkalian matriks daripada yang Anda gunakan.

Kedua, CPU Anda dapat melakukan lebih dari satu instruksi dalam satu waktu.

CPU Anda menjalankan 3-4 instruksi per siklus, dan jika unit SIMD digunakan, setiap proses instruksi 4 float atau 2 double. (tentu saja angka ini juga tidak akurat, karena CPU biasanya hanya dapat memproses satu instruksi SIMD per siklus)

Ketiga, kode Anda jauh dari optimal:

  • Anda menggunakan pointer mentah, yang berarti kompilator harus menganggapnya sebagai alias. Ada kata kunci atau tanda khusus kompilator yang dapat Anda tentukan untuk memberi tahu kompilator bahwa mereka tidak alias. Sebagai alternatif, Anda harus menggunakan jenis lain selain petunjuk mentah, yang menangani masalah.
  • Anda merontokkan cache dengan melakukan traversal naif dari setiap baris / kolom dari matriks input. Anda dapat menggunakan pemblokiran untuk melakukan pekerjaan sebanyak mungkin pada blok matriks yang lebih kecil, yang cocok dengan cache CPU, sebelum melanjutkan ke blok berikutnya.
  • Untuk tugas numerik murni, Fortran cukup banyak tidak terkalahkan, dan C ++ membutuhkan banyak bujukan untuk mendapatkan kecepatan yang sama. Ini bisa dilakukan, dan ada beberapa perpustakaan yang mendemonstrasikannya (biasanya menggunakan template ekspresi), tapi ini tidak sepele, dan itu tidak terjadi begitu saja .
jalf
sumber
Terima kasih, saya telah menambahkan kode yang benar sesuai saran Justicle, tidak melihat banyak peningkatan, saya suka ide yang menghalangi. Karena penasaran, tanpa mengetahui ukuran cache CPU bagaimana salah satu kode optimal yang tepat?
DeusAduro
2
Kamu tidak. Untuk mendapatkan kode yang optimal, Anda perlu mengetahui ukuran cache CPU. Tentu saja sisi negatifnya adalah Anda secara efektif meng-hardcode kode Anda untuk kinerja terbaik pada satu keluarga CPU.
jalf
2
Setidaknya loop dalam di sini menghindari beban langkah. Sepertinya ini ditulis untuk satu matriks yang sudah dialihkan. Itulah mengapa "hanya" satu kali lipat lebih lambat dari BLAS! Tapi ya, itu masih meronta-ronta karena kurangnya pemblokiran cache. Apakah Anda yakin Fortran akan banyak membantu? Saya pikir semua yang Anda dapatkan di sini adalah restrict(tanpa aliasing) adalah default, tidak seperti di C / C ++. (Dan sayangnya ISO C ++ tidak memiliki restrictkata kunci, jadi Anda harus menggunakannya __restrict__pada kompiler yang menyediakannya sebagai ekstensi).
Peter Cordes
11

Saya tidak tahu secara spesifik tentang implementasi BLAS tetapi ada alogoritma yang lebih efisien untuk Perkalian Matriks yang memiliki kompleksitas yang lebih baik daripada O (n3). Salah satu yang paling dikenal adalah Strassen Algorithm

softveda
sumber
8
Algoritma Strassen tidak digunakan dalam numerik karena dua alasan: 1) Tidak stabil. 2) Anda menyimpan beberapa perhitungan tetapi itu datang dengan harga yang Anda dapat mengeksploitasi hierarki cache. Dalam prakteknya Anda malah kehilangan performa.
Michael Lehn
4
Untuk implementasi praktis Algoritma Strassen yang dibangun dengan kuat di atas kode sumber pustaka BLAS, ada publikasi terbaru: " Strassen Algorithm Reloaded " di SC16, yang mencapai kinerja lebih tinggi daripada BLAS, bahkan untuk ukuran masalah 1000x1000.
Jianyu Huang
4

Sebagian besar argumen untuk pertanyaan kedua - assembler, pemisahan menjadi blok, dll. (Tetapi tidak kurang dari algoritma N ^ 3, mereka benar-benar dikembangkan secara berlebihan) - berperan. Tetapi kecepatan rendah algoritme Anda pada dasarnya disebabkan oleh ukuran matriks dan pengaturan yang tidak menguntungkan dari tiga loop bersarang. Matriks Anda sangat besar sehingga tidak dapat dimasukkan sekaligus dalam memori cache. Anda dapat mengatur ulang loop sedemikian rupa sehingga sebanyak mungkin akan dilakukan pada satu baris dalam cache, dengan cara ini secara dramatis mengurangi penyegaran cache (Pembagian BTW menjadi blok-blok kecil memiliki efek analog, paling baik jika loop di atas blok diatur serupa). Implementasi model untuk matriks persegi berikut. Di komputer saya, konsumsi waktunya sekitar 1:10 dibandingkan dengan penerapan standar (seperti milik Anda). Dengan kata lain: jangan pernah memprogram perkalian matriks sepanjang "

    void vector(int m, double ** a, double ** b, double ** c) {
      int i, j, k;
      for (i=0; i<m; i++) {
        double * ci = c[i];
        for (k=0; k<m; k++) ci[k] = 0.;
        for (j=0; j<m; j++) {
          double aij = a[i][j];
          double * bj = b[j];
          for (k=0; k<m; k++)  ci[k] += aij*bj[k];
        }
      }
    }

Satu komentar lagi: Implementasi ini bahkan lebih baik di komputer saya daripada mengganti semua dengan BLAS rutin cblas_dgemm (coba di komputer Anda!). Tetapi jauh lebih cepat (1: 4) memanggil dgemm_ dari pustaka Fortran secara langsung. Saya pikir rutinitas ini sebenarnya bukan Fortran tetapi kode assembler (saya tidak tahu apa yang ada di perpustakaan, saya tidak punya sumbernya). Sama sekali tidak jelas bagi saya mengapa cblas_dgemm tidak secepat karena sepengetahuan saya itu hanyalah pembungkus untuk dgemm_.

Wolfgang Jansen
sumber
3

Ini adalah percepatan yang realistis. Untuk contoh tentang apa yang dapat dilakukan dengan assembler SIMD melalui kode C ++, lihat beberapa contoh fungsi matriks iPhone - ini lebih dari 8x lebih cepat daripada versi C, dan bahkan tidak perakitan "dioptimalkan" - belum ada pipa-lining dan di sana adalah operasi tumpukan yang tidak perlu.

Juga kode Anda tidak " batasi yang benar " - bagaimana kompilator tahu bahwa ketika ia memodifikasi C, ia tidak memodifikasi A dan B?

Justicle
sumber
Tentu jika Anda memanggil fungsi seperti mmult (A ..., A ..., A); Anda pasti tidak akan mendapatkan hasil yang diharapkan. Sekali lagi meskipun saya tidak mencoba untuk mengalahkan / menerapkan kembali BLAS, hanya melihat seberapa cepat itu sebenarnya, jadi pemeriksaan kesalahan tidak ada dalam pikiran, hanya fungsionalitas dasar.
DeusAduro
3
Maaf, untuk memperjelas, maksud saya adalah jika Anda meletakkan "batasi" pada petunjuk Anda, Anda akan mendapatkan kode yang jauh lebih cepat. Ini karena setiap kali Anda memodifikasi C, kompiler tidak harus memuat ulang A dan B - secara dramatis mempercepat loop dalam. Jika Anda tidak percaya, periksa pembongkarannya.
Justicle
@DeusAduro: Ini bukan pemeriksaan kesalahan - mungkin saja kompilator tidak dapat mengoptimalkan akses ke array B [] di loop dalam karena mungkin tidak dapat mengetahui bahwa pointer A dan C tidak pernah alias B Himpunan. Jika ada aliasing akan memungkinkan untuk nilai dalam larik B berubah saat loop dalam sedang dijalankan. Mengangkat akses ke nilai B [] keluar dari loop dalam dan meletakkannya di variabel lokal memungkinkan kompilator untuk menghindari akses terus-menerus ke B [].
Michael Burr
1
Hmmm, jadi saya coba dulu menggunakan kata kunci '__restrict' di VS 2008, diterapkan ke A, B, dan C. Hal ini menunjukkan tidak ada perubahan pada hasil. Namun memindahkan akses ke B, dari loop terdalam ke loop luar meningkatkan waktu sebesar ~ 10%.
DeusAduro
1
Maaf, saya tidak yakin tentang VC, tapi dengan GCC Anda harus mengaktifkannya -fstrict-aliasing. Ada juga penjelasan yang lebih baik tentang "batasi" di sini: cellperformance.beyond3d.com/articles/2006/05/…
Justicle
2

Sehubungan dengan kode asli dalam perkalian MM, referensi memori untuk sebagian besar operasi adalah penyebab utama kinerja buruk. Memori berjalan 100-1000 kali lebih lambat dari cache.

Sebagian besar percepatan berasal dari penggunaan teknik pengoptimalan loop untuk fungsi triple loop ini dalam perkalian MM. Dua teknik optimasi loop utama digunakan; membuka gulungan dan memblokir. Sehubungan dengan unrolling, kami membuka gulungan dua loop terluar dan memblokirnya agar data digunakan kembali di cache. Pembukaan loop luar membantu mengoptimalkan akses data secara temporer dengan mengurangi jumlah referensi memori ke data yang sama pada waktu yang berbeda selama keseluruhan operasi. Memblokir indeks loop pada nomor tertentu, membantu mempertahankan data dalam cache. Anda dapat memilih untuk mengoptimalkan L2 cache atau L3 cache.

https://en.wikipedia.org/wiki/Loop_nest_optimization

Pari Rajaram
sumber
-24

Untuk banyak alasan.

Pertama, kompiler Fortran sangat dioptimalkan, dan bahasanya memungkinkan mereka untuk menjadi seperti itu. C dan C ++ sangat longgar dalam hal penanganan larik (misalnya kasus pointer yang merujuk ke area memori yang sama). Ini berarti kompilator tidak dapat mengetahui lebih dulu apa yang harus dilakukan, dan dipaksa untuk membuat kode generik. Di Fortran, kasus Anda lebih efisien, dan kompilator memiliki kendali yang lebih baik atas apa yang terjadi, memungkinkannya untuk lebih mengoptimalkan (misalnya menggunakan register).

Hal lain adalah Fortran menyimpan barang secara kolom, sementara C menyimpan data berdasarkan baris. Saya belum 'memeriksa kode Anda, tetapi hati-hati dengan cara Anda menjalankan produk. Di C Anda harus memindai baris dengan bijak: dengan cara ini Anda memindai array Anda di sepanjang memori yang berdekatan, mengurangi cache yang meleset. Cache miss adalah sumber inefisiensi pertama.

Ketiga, tergantung dari implementasi blas yang Anda gunakan. Beberapa implementasi mungkin ditulis dalam assembler, dan dioptimalkan untuk prosesor tertentu yang Anda gunakan. Versi netlib ditulis di fortran 77.

Juga, Anda melakukan banyak operasi, kebanyakan diulang dan mubazir. Semua perkalian untuk mendapatkan indeks tersebut merugikan kinerja. Saya tidak begitu tahu bagaimana hal ini dilakukan di BLAS, tetapi ada banyak trik untuk mencegah operasi yang mahal.

Misalnya, Anda dapat mengerjakan ulang kode Anda dengan cara ini

template<class ValT>
void mmult(const ValT* A, int ADim1, int ADim2, const ValT* B, int BDim1, int BDim2, ValT* C)
{
if ( ADim2!=BDim1 ) throw std::runtime_error("Error sizes off");

memset((void*)C,0,sizeof(ValT)*ADim1*BDim2);
int cc2,cc1,cr1, a1,a2,a3;
for ( cc2=0 ; cc2<BDim2 ; ++cc2 ) {
    a1 = cc2*ADim2;
    a3 = cc2*BDim1
    for ( cc1=0 ; cc1<ADim2 ; ++cc1 ) {
          a2=cc1*ADim1;
          ValT b = B[a3+cc1];
          for ( cr1=0 ; cr1<ADim1 ; ++cr1 ) {
                    C[a1+cr1] += A[a2+cr1]*b;
           }
     }
  }
} 

Cobalah, saya yakin Anda akan menyelamatkan sesuatu.

Pada pertanyaan # 1 Anda, alasannya adalah bahwa perkalian matriks berskala sebagai O (n ^ 3) jika Anda menggunakan algoritme yang sepele. Ada algoritme yang berskala jauh lebih baik .

Stefano Borini
sumber
36
Jawaban ini salah, maaf. Implementasi BLAS tidak ditulis dalam fortran. Kode performence-critical ditulis dalam assembly, dan yang paling umum saat ini ditulis dalam C di atasnya. BLAS juga menentukan urutan baris / kolom sebagai bagian dari antarmuka, dan implementasi dapat menangani kombinasi apa pun.
Andrew Tomazos
10
Ya, jawaban ini sepenuhnya salah. Sayangnya itu penuh dengan nalar umum, misalnya klaim BLAS lebih cepat karena Fortran. Memiliki 20 peringkat positif (!) Adalah hal yang buruk. Sekarang hal yang tidak masuk akal ini bahkan menyebar lebih jauh karena popularitas Stackoverflow!
Michael Lehn
12
Saya rasa Anda bingung antara implementasi referensi yang tidak dioptimalkan dengan implementasi produksi. Implementasi referensi hanya untuk menentukan antarmuka dan perilaku perpustakaan, dan ditulis di Fortran karena alasan historis. Ini bukan untuk penggunaan produksi. Dalam produksi, orang menggunakan implementasi yang dioptimalkan yang menunjukkan perilaku yang sama dengan implementasi referensi. Saya telah mempelajari internal ATLAS (yang mendukung Octave - Linux "MATLAB") yang dapat saya konfirmasikan secara langsung ditulis dalam C / ASM secara internal. Penerapan komersial hampir pasti juga.
Andrew Tomazos
5
@KyleKanos: Ya, berikut adalah sumber ATLAS: sourceforge.net/projects/math-atlas/files/Stable/3.10.1 Sejauh yang saya tahu, ini adalah implementasi BLAS portabel open source yang paling umum digunakan. Itu ditulis dalam C / ASM. Produsen CPU berkinerja tinggi seperti Intel, juga menyediakan implementasi BLAS yang dioptimalkan secara khusus untuk chip mereka. Saya jamin di bagian tingkat rendah pustaka Intels ditulis dalam rakitan (duuh) x86, dan saya cukup yakin bagian tingkat menengah akan ditulis dalam C atau C ++.
Andrew Tomazos
9
@KKos: Anda bingung. Netlib BLAS adalah implementasi referensi. Implementasi referensi jauh lebih lambat daripada implementasi yang dioptimalkan (lihat perbandingan performa ). Ketika seseorang mengatakan mereka menggunakan netlib BLAS di sebuah cluster, itu tidak berarti mereka sebenarnya menggunakan implementasi referensi netlib. Itu konyol. Ini hanya berarti mereka menggunakan lib dengan antarmuka yang sama dengan netlib blas.
Andrew Tomazos