<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.haidhi.my.id/wiki/index.php?action=history&amp;feed=atom&amp;title=BLAS%3A_Level_1%2C_2%2C_3</id>
	<title>BLAS: Level 1, 2, 3 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.haidhi.my.id/wiki/index.php?action=history&amp;feed=atom&amp;title=BLAS%3A_Level_1%2C_2%2C_3"/>
	<link rel="alternate" type="text/html" href="https://wiki.haidhi.my.id/wiki/index.php?title=BLAS:_Level_1,_2,_3&amp;action=history"/>
	<updated>2026-11-01T20:53:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.haidhi.my.id/wiki/index.php?title=BLAS:_Level_1,_2,_3&amp;diff=798&amp;oldid=prev</id>
		<title>SanzSkuy: Created page with &quot;&#039;&#039;&#039;BLAS&#039;&#039;&#039; (Basic Linear Algebra Subprograms) adalah kosakata standar operasi aljabar linear padat (dense): perkalian vektor, matriks-vektor, matriks-matriks, dan kawan-kawannya. Hampir semua yang ada di atasnya — LAPACK, NumPy, PyTorch, HPL — pada akhirnya memanggil BLAS. Pembagian &#039;&#039;&#039;Level 1, 2, 3&#039;&#039;&#039; awalnya cuma urutan sejarah, tapi kebetulan (dan ini bagian pentingnya) membelah dunia berdasarkan &#039;&#039;&#039;arithmetic intensity&#039;&#039;&#039;: Level 1–2 se...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.haidhi.my.id/wiki/index.php?title=BLAS:_Level_1,_2,_3&amp;diff=798&amp;oldid=prev"/>
		<updated>2026-07-11T11:41:54Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;&amp;#039;&amp;#039;&amp;#039;BLAS&amp;#039;&amp;#039;&amp;#039; (Basic Linear Algebra Subprograms) adalah kosakata standar operasi aljabar linear padat (dense): perkalian vektor, matriks-vektor, matriks-matriks, dan kawan-kawannya. Hampir semua yang ada di atasnya — LAPACK, NumPy, PyTorch, &lt;a href=&quot;/wiki/index.php?title=Benchmark:_HPL_%26_HPCG&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;Benchmark: HPL &amp;amp; HPCG (page does not exist)&quot;&gt;HPL&lt;/a&gt; — pada akhirnya memanggil BLAS. Pembagian &amp;#039;&amp;#039;&amp;#039;Level 1, 2, 3&amp;#039;&amp;#039;&amp;#039; awalnya cuma urutan sejarah, tapi kebetulan (dan ini bagian pentingnya) membelah dunia berdasarkan &amp;#039;&amp;#039;&amp;#039;arithmetic intensity&amp;#039;&amp;#039;&amp;#039;: Level 1–2 se...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;BLAS&amp;#039;&amp;#039;&amp;#039; (Basic Linear Algebra Subprograms) adalah kosakata standar operasi aljabar linear padat (dense): perkalian vektor, matriks-vektor, matriks-matriks, dan kawan-kawannya. Hampir semua yang ada di atasnya — LAPACK, NumPy, PyTorch, [[Benchmark: HPL &amp;amp; HPCG|HPL]] — pada akhirnya memanggil BLAS. Pembagian &amp;#039;&amp;#039;&amp;#039;Level 1, 2, 3&amp;#039;&amp;#039;&amp;#039; awalnya cuma urutan sejarah, tapi kebetulan (dan ini bagian pentingnya) membelah dunia berdasarkan &amp;#039;&amp;#039;&amp;#039;arithmetic intensity&amp;#039;&amp;#039;&amp;#039;: Level 1–2 selamanya terkurung bandwidth memori, Level 3 satu-satunya yang bisa menyentuh puncak FLOPS mesin.&lt;br /&gt;
&lt;br /&gt;
Prasyarat enak: [[GFLOPS]] dan [[Roofline model]].&lt;br /&gt;
&lt;br /&gt;
= Tiga Level =&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! !! Tahun !! Operasi !! Contoh !! Flops !! Data !! Intensitas !! Terikat oleh&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Level 1&amp;#039;&amp;#039;&amp;#039; || 1979 || vektor–vektor || &amp;lt;code&amp;gt;axpy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;dot&amp;lt;/code&amp;gt; || O(n) || O(n) || ~0.08 flop/byte || bandwidth&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Level 2&amp;#039;&amp;#039;&amp;#039; || 1988 || matriks–vektor || &amp;lt;code&amp;gt;gemv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;trsv&amp;lt;/code&amp;gt; || O(n²) || O(n²) || ~0.25 flop/byte || bandwidth&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Level 3&amp;#039;&amp;#039;&amp;#039; || 1990 || matriks–matriks || &amp;lt;code&amp;gt;gemm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;trsm&amp;lt;/code&amp;gt; || O(n³) || O(n²) || ~n/16 flop/byte || &amp;#039;&amp;#039;&amp;#039;compute&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Logika intensitasnya:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Level 1&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;daxpy&amp;lt;/code&amp;gt; (y ← αx + y): tiap elemen dibaca sekali, dipakai untuk 2 flop, selesai. 2n flop untuk ~24n byte lalu lintas → 0.083 flop/byte. Mau prosesor secepat apa pun, kecepatannya = kecepatan RAM.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Level 2&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;dgemv&amp;lt;/code&amp;gt; (y ← αAx + βy): matriks A (8n² byte) dibaca sekali untuk 2n² flop → 0.25 flop/byte. Masih nasib yang sama.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Level 3&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt; (C ← αAB + βC): 2n³ flop atas data hanya O(n²) — &amp;#039;&amp;#039;&amp;#039;tiap elemen A dan B dipakai n kali&amp;#039;&amp;#039;&amp;#039;. Dengan blocking yang benar, data bisa parkir di cache dan dipakai berulang; intensitas naik sebanding n. Inilah satu-satunya operasi BLAS yang bisa lari di atap compute [[Roofline model|roofline]].&lt;br /&gt;
&lt;br /&gt;
Satu kalimat yang layak dihafal: &amp;#039;&amp;#039;&amp;#039;&amp;quot;Level 3 bukan sekadar lebih banyak kerja — dia kerja yang bisa di-cache.&amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
= Membaca Nama Rutin =&lt;br /&gt;
&lt;br /&gt;
Nama BLAS kelihatan seperti kucing jalan di keyboard (&amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ztrsv&amp;lt;/code&amp;gt;) padahal sistematis: &amp;#039;&amp;#039;&amp;#039;presisi + jenis matriks + operasi&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bagian !! Arti&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;z&amp;lt;/code&amp;gt; || float / double / complex / double complex&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ge&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;sy&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;he&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;gb&amp;lt;/code&amp;gt; || general / symmetric / hermitian / triangular / general banded&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mm&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;sv&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;rk&amp;lt;/code&amp;gt; || matrix-matrix / matrix-vector / solve / rank-k update&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Jadi &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt; = &amp;#039;&amp;#039;&amp;#039;d&amp;#039;&amp;#039;&amp;#039;ouble &amp;#039;&amp;#039;&amp;#039;ge&amp;#039;&amp;#039;&amp;#039;neral &amp;#039;&amp;#039;&amp;#039;m&amp;#039;&amp;#039;&amp;#039;atrix-&amp;#039;&amp;#039;&amp;#039;m&amp;#039;&amp;#039;&amp;#039;atrix multiply, dan &amp;lt;code&amp;gt;ztrsv&amp;lt;/code&amp;gt; = double-complex triangular solve dengan vektor. Rutin yang paling sering ketemu:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Level !! Rutin !! Rumus&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;daxpy&amp;lt;/code&amp;gt; || y ← αx + y&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;ddot&amp;lt;/code&amp;gt; || hasil ← xᵀy&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;dscal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;dnrm2&amp;lt;/code&amp;gt; || x ← αx; ‖x‖₂&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;dgemv&amp;lt;/code&amp;gt; || y ← αAx + βy&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;dger&amp;lt;/code&amp;gt; || A ← αxyᵀ + A (rank-1 update)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;dtrsv&amp;lt;/code&amp;gt; || selesaikan Tx = b, T segitiga&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt; || C ← αAB + βC (op(A), op(B) bisa transpose)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;dtrsm&amp;lt;/code&amp;gt; || selesaikan TX = B untuk banyak kolom sekaligus&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;dsyrk&amp;lt;/code&amp;gt; || C ← αAAᵀ + βC&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Warisan Fortran: Column-Major &amp;amp; Leading Dimension =&lt;br /&gt;
&lt;br /&gt;
BLAS lahir dari Fortran, jadi API aslinya berasumsi matriks tersimpan &amp;#039;&amp;#039;&amp;#039;column-major&amp;#039;&amp;#039;&amp;#039; (kolom demi kolom di memori). C/C++ terbiasa row-major. Interface C-nya, &amp;#039;&amp;#039;&amp;#039;CBLAS&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;&amp;lt;cblas.h&amp;gt;&amp;lt;/code&amp;gt;), memberi parameter pertama &amp;lt;code&amp;gt;CblasRowMajor&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;CblasColMajor&amp;lt;/code&amp;gt; supaya tidak perlu mentranspose kepala sendiri.&lt;br /&gt;
&lt;br /&gt;
Parameter yang paling sering bikin bingung: &amp;#039;&amp;#039;&amp;#039;leading dimension&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;lda&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ldb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ldc&amp;lt;/code&amp;gt;) = jarak (dalam elemen) antara awal baris berturut-turut (row-major) atau kolom berturut-turut (col-major) di memori. Untuk matriks utuh, &amp;lt;code&amp;gt;lda&amp;lt;/code&amp;gt; = lebar alokasinya. Gunanya: mengoperasikan &amp;#039;&amp;#039;&amp;#039;submatriks&amp;#039;&amp;#039;&amp;#039; tanpa menyalin —&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
// A_besar berukuran M x N (row-major). Kita mau blok m x k yang&lt;br /&gt;
// pojok kirinya di (bi, bj): cukup geser pointer + lda tetap N.&lt;br /&gt;
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,&lt;br /&gt;
            m, n_blok, k,&lt;br /&gt;
            1.0, A_besar + bi * N + bj, N,   // lda = N, BUKAN k!&lt;br /&gt;
                 B, ldb, 0.0, C, ldc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Salah mengisi &amp;lt;code&amp;gt;lda&amp;lt;/code&amp;gt; tidak selalu crash — sering cuma hasil salah diam-diam. Kalau hasil &amp;lt;code&amp;gt;gemm&amp;lt;/code&amp;gt; terlihat seperti sampah terstruktur, cek urutan major dan leading dimension dulu.&lt;br /&gt;
&lt;br /&gt;
= Mengukur Ketiga Level =&lt;br /&gt;
&lt;br /&gt;
Program lengkap: satu operasi wakil tiap level, ukur GFLOPS-nya. Compile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
g++ -O2 -fopenmp -o blas_level blas_level.cpp -lcblas&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdio&amp;gt;&lt;br /&gt;
#include &amp;lt;cblas.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Ukur GFLOPS satu operasi wakil tiap level BLAS.&lt;br /&gt;
// Level 1: daxpy  (2n flops, ~24n byte)    -&amp;gt; intensitas ~0.08 flop/byte&lt;br /&gt;
// Level 2: dgemv  (2n^2 flops, ~8n^2 byte) -&amp;gt; intensitas ~0.25 flop/byte&lt;br /&gt;
// Level 3: dgemm  (2n^3 flops, ~32n^2 byte)-&amp;gt; intensitas ~n/16 flop/byte&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
    // ---- Level 1: y = a*x + y ----&lt;br /&gt;
    {&lt;br /&gt;
        const long n = 1L &amp;lt;&amp;lt; 26;                    // 2 array x 512 MiB&lt;br /&gt;
        double *x = new double[n], *y = new double[n];&lt;br /&gt;
        #pragma omp parallel for schedule(static)&lt;br /&gt;
        for (long i = 0; i &amp;lt; n; ++i) { x[i] = 1.0; y[i] = 2.0; }&lt;br /&gt;
&lt;br /&gt;
        double best = 1e99;&lt;br /&gt;
        for (int u = 0; u &amp;lt; 5; ++u) {&lt;br /&gt;
            double t0 = omp_get_wtime();&lt;br /&gt;
            cblas_daxpy(n, 3.0, x, 1, y, 1);&lt;br /&gt;
            double t1 = omp_get_wtime();&lt;br /&gt;
            if (t1 - t0 &amp;lt; best) best = t1 - t0;&lt;br /&gt;
        }&lt;br /&gt;
        printf(&amp;quot;L1 daxpy  n=%-9ld : %7.2f GFLOPS  (%.1f GB/s)\n&amp;quot;,&lt;br /&gt;
               n, 2.0 * n / best / 1e9, 24.0 * n / best / 1e9);&lt;br /&gt;
        delete[] x; delete[] y;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ---- Level 2: y = A*x ----&lt;br /&gt;
    {&lt;br /&gt;
        const long n = 8192;                        // A = 512 MiB&lt;br /&gt;
        double *A = new double[n * n], *x = new double[n], *y = new double[n];&lt;br /&gt;
        #pragma omp parallel for schedule(static)&lt;br /&gt;
        for (long i = 0; i &amp;lt; n * n; ++i) A[i] = 1e-3;&lt;br /&gt;
        for (long i = 0; i &amp;lt; n; ++i) { x[i] = 1.0; y[i] = 0.0; }&lt;br /&gt;
&lt;br /&gt;
        double best = 1e99;&lt;br /&gt;
        for (int u = 0; u &amp;lt; 5; ++u) {&lt;br /&gt;
            double t0 = omp_get_wtime();&lt;br /&gt;
            cblas_dgemv(CblasRowMajor, CblasNoTrans, n, n,&lt;br /&gt;
                        1.0, A, n, x, 1, 0.0, y, 1);&lt;br /&gt;
            double t1 = omp_get_wtime();&lt;br /&gt;
            if (t1 - t0 &amp;lt; best) best = t1 - t0;&lt;br /&gt;
        }&lt;br /&gt;
        printf(&amp;quot;L2 dgemv  n=%-9ld : %7.2f GFLOPS  (%.1f GB/s)\n&amp;quot;,&lt;br /&gt;
               n, 2.0 * n * n / best / 1e9, 8.0 * n * n / best / 1e9);&lt;br /&gt;
        delete[] A; delete[] x; delete[] y;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ---- Level 3: C = A*B ----&lt;br /&gt;
    {&lt;br /&gt;
        const long n = 1024;&lt;br /&gt;
        double *A = new double[n * n], *B = new double[n * n], *C = new double[n * n];&lt;br /&gt;
        for (long i = 0; i &amp;lt; n * n; ++i) { A[i] = 1e-3; B[i] = 1e-3; C[i] = 0.0; }&lt;br /&gt;
&lt;br /&gt;
        double best = 1e99;&lt;br /&gt;
        for (int u = 0; u &amp;lt; 3; ++u) {&lt;br /&gt;
            double t0 = omp_get_wtime();&lt;br /&gt;
            cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n, n, n,&lt;br /&gt;
                        1.0, A, n, B, n, 0.0, C, n);&lt;br /&gt;
            double t1 = omp_get_wtime();&lt;br /&gt;
            if (t1 - t0 &amp;lt; best) best = t1 - t0;&lt;br /&gt;
        }&lt;br /&gt;
        printf(&amp;quot;L3 dgemm  n=%-9ld : %7.2f GFLOPS\n&amp;quot;, n, 2.0 * n * n * n / best / 1e9);&lt;br /&gt;
        delete[] A; delete[] B; delete[] C;&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hasil nyata (i5-13450HX, paket Arch &amp;lt;code&amp;gt;cblas&amp;lt;/code&amp;gt; = &amp;#039;&amp;#039;&amp;#039;reference BLAS dari Netlib&amp;#039;&amp;#039;&amp;#039;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
L1 daxpy  n=67108864  :    2.47 GFLOPS  (29.6 GB/s)&lt;br /&gt;
L2 dgemv  n=8192      :    2.93 GFLOPS  (11.7 GB/s)&lt;br /&gt;
L3 dgemm  n=1024      :    3.77 GFLOPS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bacaannya:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;daxpy: 29.6 GB/s&amp;#039;&amp;#039;&amp;#039; ≈ bandwidth memori mesin ini (bandingkan triad ~25 GB/s di [[OpenMP: Affinity &amp;amp; First-Touch]]). Level 1 sudah mentok di atap bandwidth — &amp;#039;&amp;#039;&amp;#039;tidak ada implementasi yang bisa membuatnya jauh lebih cepat&amp;#039;&amp;#039;&amp;#039;, fisika sudah bicara.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;dgemv&amp;#039;&amp;#039;&amp;#039; setali tiga uang, terikat laju pembacaan A.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;dgemm: 3.77 GFLOPS.&amp;#039;&amp;#039;&amp;#039; Nah ini skandal. Level 3 harusnya compute-bound — mesin ini sanggup ratusan GFLOPS — tapi cuma dapat 4-an. Kenapa? Lanjut ke bagian berikut.&lt;br /&gt;
&lt;br /&gt;
= BLAS Adalah Interface, Bukan Library =&lt;br /&gt;
&lt;br /&gt;
BLAS hanya mendefinisikan &amp;#039;&amp;#039;apa&amp;#039;&amp;#039; fungsinya; &amp;#039;&amp;#039;seberapa cepat&amp;#039;&amp;#039; sepenuhnya urusan implementasi:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Implementasi !! Catatan&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Reference (Netlib)&amp;#039;&amp;#039;&amp;#039; || Fortran lurus, tanpa blocking/SIMD/threading. Tolok ukur &amp;#039;&amp;#039;&amp;#039;kebenaran&amp;#039;&amp;#039;&amp;#039;, bukan kecepatan. Di Arch ini paket &amp;lt;code&amp;gt;blas&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;cblas&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;OpenBLAS&amp;#039;&amp;#039;&amp;#039; || Open source, assembly kernel per arsitektur, multithread. Standar de-facto di Linux. (Arch: &amp;lt;code&amp;gt;openblas&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;blas-openblas&amp;lt;/code&amp;gt;.)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Intel MKL&amp;#039;&amp;#039;&amp;#039; || Tercepat di CPU Intel; gratis dipakai.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;BLIS&amp;#039;&amp;#039;&amp;#039; || Framework riset modern, basis AMD AOCL.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;cuBLAS&amp;#039;&amp;#039;&amp;#039; || NVIDIA GPU — dipanggil dari host, kerjanya di device.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Bukti bahwa implementasi adalah segalanya — lomba &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt; n=1024, empat kandidat:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
// 1. Tiga loop polos, urutan ijk (inner loop melompati B selebar n)&lt;br /&gt;
void naive_ijk(const double *A, const double *B, double *C) {&lt;br /&gt;
    for (long i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
        for (long j = 0; j &amp;lt; n; ++j) {&lt;br /&gt;
            double s = 0.0;&lt;br /&gt;
            for (long k = 0; k &amp;lt; n; ++k)&lt;br /&gt;
                s += A[i * n + k] * B[k * n + j];&lt;br /&gt;
            C[i * n + j] = s;&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 2. Loop yang sama, urutan ikj (inner loop kontigu -&amp;gt; SIMD &amp;amp; cache jalan)&lt;br /&gt;
void reorder_ikj(const double *A, const double *B, double *C) {&lt;br /&gt;
    for (long i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
        for (long k = 0; k &amp;lt; n; ++k) {&lt;br /&gt;
            const double a = A[i * n + k];&lt;br /&gt;
            for (long j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
                C[i * n + j] += a * B[k * n + j];&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 3. ikj + OpenMP di loop terluar&lt;br /&gt;
void omp_ikj(const double *A, const double *B, double *C) {&lt;br /&gt;
    #pragma omp parallel for schedule(static)&lt;br /&gt;
    for (long i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
        for (long k = 0; k &amp;lt; n; ++k) {&lt;br /&gt;
            const double a = A[i * n + k];&lt;br /&gt;
            for (long j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
                C[i * n + j] += a * B[k * n + j];&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 4. Serahkan ke profesional&lt;br /&gt;
void blas_dgemm(const double *A, const double *B, double *C) {&lt;br /&gt;
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n, n, n,&lt;br /&gt;
                1.0, A, n, B, n, 0.0, C, n);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hasil nyata (16 thread):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
n = 1024, threads = 16&lt;br /&gt;
&lt;br /&gt;
naive ijk          :   3.294 s     0.65 GFLOPS&lt;br /&gt;
reorder ikj        :   0.436 s     4.92 GFLOPS&lt;br /&gt;
ikj + OpenMP       :   0.048 s    45.03 GFLOPS&lt;br /&gt;
cblas_dgemm (ref)  :   0.406 s     5.29 GFLOPS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Empat pelajaran dari satu tabel:&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Urutan loop = 7.6×.&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ijk&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;ikj&amp;lt;/code&amp;gt; mengubah akses B dari lompat-lompat selebar baris jadi kontigu: [[Cache line]] terpakai penuh dan compiler berani auto-vectorize ([[SIMD: Auto-Vectorization &amp;amp; Laporan Compiler|SIMD]]). Belum ada paralelisme sama sekali.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;OpenMP menambah 9×&amp;#039;&amp;#039;&amp;#039; lagi di 16 thread.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Kode 20 baris kita mengalahkan BLAS terinstall 8.5×.&amp;#039;&amp;#039;&amp;#039; Bukan karena kita hebat — karena yang terinstall reference BLAS. Ini jebakan nyata: di banyak distro dan cluster, &amp;lt;code&amp;gt;-lblas&amp;lt;/code&amp;gt; default menaut ke Netlib. &amp;#039;&amp;#039;&amp;#039;Selalu cek implementasi apa yang sebenarnya ke-link&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ldd ./program&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;45 GFLOPS itu tetap kelas amatir.&amp;#039;&amp;#039;&amp;#039; OpenBLAS/MKL di mesin yang sama tipikalnya ratusan GFLOPS (puncak teoretis mesin ini ~400+ GFLOPS DP) lewat blocking bertingkat mengikuti hierarki cache + microkernel assembly + packing. Teknik blocking-nya persis ide yang sama dengan [[CUDA: Matmul dengan Tiling]] — beda alamat, ide serupa.&lt;br /&gt;
&lt;br /&gt;
Moral: jangan pernah menulis &amp;lt;code&amp;gt;gemm&amp;lt;/code&amp;gt; sendiri untuk produksi. Tulis sekali untuk belajar (seperti di atas), lalu panggil library selamanya.&lt;br /&gt;
&lt;br /&gt;
= Kenapa Level 3 Itu Raja =&lt;br /&gt;
&lt;br /&gt;
Karena hanya Level 3 yang bisa lari di atap compute, seni menyusun algoritma numerik = &amp;#039;&amp;#039;&amp;#039;memindahkan sebanyak mungkin kerja ke Level 3&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* LAPACK menyusun ulang faktorisasi ([[Dekomposisi LU &amp;amp; Cholesky]]) jadi versi &amp;#039;&amp;#039;blocked&amp;#039;&amp;#039;: panel difaktorkan dengan Level 2 (porsi kecil), lalu sisanya di-update besar-besaran via &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;dtrsm&amp;lt;/code&amp;gt; (porsi raksasa). Rasio kerja Level 3-nya mendekati 100% saat matriks besar.&lt;br /&gt;
* Deep learning duduk di atas &amp;lt;code&amp;gt;gemm&amp;lt;/code&amp;gt;: layer fully-connected = gemm, convolution di-lower jadi gemm (im2col), attention = gemm bertumpuk. GPU tensor core pun pada dasarnya mesin gemm.&lt;br /&gt;
* Pola praktis: &amp;#039;&amp;#039;&amp;#039;kalau kode memanggil &amp;lt;code&amp;gt;gemv&amp;lt;/code&amp;gt; berulang dengan matriks yang sama, tumpuk vektor-vektornya jadi matriks dan panggil &amp;lt;code&amp;gt;gemm&amp;lt;/code&amp;gt; sekali.&amp;#039;&amp;#039;&amp;#039; Kerjanya (flop) sama persis; lalu lintas memorinya runtuh karena A dibaca sekali, bukan k kali.&lt;br /&gt;
&lt;br /&gt;
Catatan batas wilayah: semua ini dunia &amp;#039;&amp;#039;&amp;#039;dense&amp;#039;&amp;#039;&amp;#039;. Begitu matriksnya sparse (mayoritas nol — tipikal PDE, graf), format dan pustakanya beda total: [[Sparse Matrix: Format CSR &amp;amp; CSC]] dan [[SpMV]] — yang sayangnya kembali bernasib Level-2: memory-bound.&lt;br /&gt;
&lt;br /&gt;
= Jebakan Umum =&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Menaut reference BLAS tanpa sadar.&amp;#039;&amp;#039;&amp;#039; Gejala: gemm &amp;quot;cuma&amp;quot; beberapa GFLOPS (terbukti di atas). Diagnosa: &amp;lt;code&amp;gt;ldd ./program | grep blas&amp;lt;/code&amp;gt;. Obat di Arch: pasang &amp;lt;code&amp;gt;openblas&amp;lt;/code&amp;gt; + link &amp;lt;code&amp;gt;-lopenblas&amp;lt;/code&amp;gt; (atau paket &amp;lt;code&amp;gt;blas-openblas&amp;lt;/code&amp;gt; yang menggantikan provider &amp;lt;code&amp;gt;libblas.so&amp;lt;/code&amp;gt; sekalian).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Row-major vs column-major kebalik.&amp;#039;&amp;#039;&amp;#039; Hasilnya transpose diam-diam. Pakai CBLAS dan konsisten &amp;lt;code&amp;gt;CblasRowMajor&amp;lt;/code&amp;gt;, atau manfaatkan identitas (AB)ᵀ = BᵀAᵀ.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Perang thread.&amp;#039;&amp;#039;&amp;#039; OpenBLAS punya thread pool sendiri. Memanggil BLAS dari dalam region [[OpenMP: Parallel For &amp;amp; Scheduling|OpenMP]] = oversubscription (16×16 thread). Aturan: BLAS multithread &amp;#039;&amp;#039;di luar&amp;#039;&amp;#039; region paralel, atau set &amp;lt;code&amp;gt;OPENBLAS_NUM_THREADS=1&amp;lt;/code&amp;gt; saat dipanggil dari dalam.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Matriks kecil.&amp;#039;&amp;#039;&amp;#039; Overhead pemanggilan + setup mengalahkan kerjanya untuk n ≲ 50-an; kalau punya ribuan matriks kecil, cari &amp;#039;&amp;#039;batched BLAS&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;cublasDgemmBatched&amp;lt;/code&amp;gt;, MKL batch API), jangan loop.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lupa β.&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt; menghitung C ← αAB + &amp;#039;&amp;#039;&amp;#039;β&amp;#039;&amp;#039;&amp;#039;C. β=0 menimpa C (dan tidak membaca isinya), β=1 mengakumulasi. Salah pilih = bug hasil yang halus.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Increment (incx) eksotis.&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;daxpy&amp;lt;/code&amp;gt; dkk. menerima stride antar elemen. Berguna untuk kolom di matriks row-major, tapi stride besar = tiap elemen satu cache line = bandwidth efektif ambruk 8×.&lt;br /&gt;
&lt;br /&gt;
= Ringkasan =&lt;br /&gt;
&lt;br /&gt;
* Level = kelas intensitas aritmetika: L1 (vektor, 0.08 flop/byte) dan L2 (matriks-vektor, 0.25) terkurung bandwidth; hanya L3 (matriks-matriks, ~n/16) yang bisa menyentuh puncak FLOPS.&lt;br /&gt;
* Nama rutin = presisi + jenis matriks + operasi: &amp;lt;code&amp;gt;dgemm&amp;lt;/code&amp;gt; = double, general, matrix-matrix.&lt;br /&gt;
* BLAS itu interface. Reference Netlib untuk kebenaran; OpenBLAS/MKL/cuBLAS untuk kecepatan. Cek &amp;lt;code&amp;gt;ldd&amp;lt;/code&amp;gt;, jangan berasumsi.&lt;br /&gt;
* Terbukti di mesin sendiri: loop order 7.6×, +OpenMP 9×, dan reference BLAS kalah dari 20 baris kode — tapi implementasi serius masih ~8× di atasnya lagi.&lt;br /&gt;
* Strategi algoritma numerik modern: dorong semua kerja ke Level 3.&lt;br /&gt;
&lt;br /&gt;
= Lihat Juga =&lt;br /&gt;
&lt;br /&gt;
* [[GFLOPS]] dan [[Roofline model]] — kerangka membaca angka-angka di atas&lt;br /&gt;
* [[Cache line]] dan [[SIMD]] — kenapa urutan loop berdampak 7.6×&lt;br /&gt;
* [[Dekomposisi LU &amp;amp; Cholesky]] — konsumen terbesar Level 3&lt;br /&gt;
* [[Sparse Matrix: Format CSR &amp;amp; CSC]], [[SpMV]] — dunia sebelah, saat matriks kebanyakan nol&lt;br /&gt;
* [[CUDA: Matmul dengan Tiling]] — blocking yang sama, di GPU&lt;br /&gt;
* [[Benchmark: HPL &amp;amp; HPCG]] — HPL pada dasarnya kontes dgemm sedunia&lt;br /&gt;
* [[OpenMP: Parallel For &amp;amp; Scheduling]] — dipakai di versi 45 GFLOPS di atas&lt;br /&gt;
&lt;br /&gt;
[[Kategori:HPC]]&lt;br /&gt;
[[Kategori:Metode Numerik]]&lt;/div&gt;</summary>
		<author><name>SanzSkuy</name></author>
	</entry>
</feed>