CodeBlanks Logo Icon CodeBlanks

SIMD matrix

Do parallel matrix multiplication with SIMD.

Final task: SIMD matrix

Write a 4×4 matrix-vector product in a SIMD-friendly form. The classic row-dot approach needs a horizontal reduction for every row. The column approach uses only vertical operations: the result vector is the sum of each column multiplied by the corresponding x component (y += column_j * x[j], "axpy"):

// y = A·x, A in column order: a[j] = column j
fn matvec(a: [[f32; 4]; 4], x: [f32; 4]) -> [f32; 4] {
    let mut y = [0.0f32; 4];
    for j in 0..4 {
        for i in 0..4 {          // a 4-lane vertical multiply-add
            y[i] += a[j][i] * x[j];
        }
    }
    y
}

The inner loop maps directly onto a single 4-lane multiply-add instruction; no horizontal operation is needed.

Verification

An optimized kernel must always be compared with a simple reference implementation. A reference giving the same result with a row-ordered matrix:

fn matvec_ref(satirlar: [[f32; 4]; 4], x: [f32; 4]) -> [f32; 4] {
    satirlar.map(|r| r.iter().zip(&x).map(|(a, b)| a * b).sum())
}

// converting to column order: the transpose
fn devrik(m: [[f32; 4]; 4]) -> [[f32; 4]; 4] {
    std::array::from_fn(|j| std::array::from_fn(|i| m[i][j]))
}

Practice Matrix-vector

Write the matvec, matvec_ref and devrik functions above. For the row-ordered [[1,2,0,0],[0,1,0,0],[0,0,2,0],[0,0,0,3]] and x = [1, 1, 1, 1], print the result of matvec(devrik(A), x) with {:?} and its equality with the reference, separated by a space.

Starter code

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
fn matvec(a: [[f32; 4]; 4], x: [f32; 4]) -> [f32; 4] {
    let mut y = [0.0f32; 4];
    for j in 0..4 {
        for i in 0..4 {
            y[i] += a[j][i] * x[j];
        }
    }
    y
}

fn matvec_ref(satirlar: [[f32; 4]; 4], x: [f32; 4]) -> [f32; 4] {
    satirlar.map(|r| r.iter().zip(&x).map(|(a, b)| a * b).sum())
}

fn devrik(m: [[f32; 4]; 4]) -> [[f32; 4]; 4] {
    std::array::from_fn(|j| std::array::from_fn(|i| m[i][j]))
}

fn main() {
    let a = [
        [1.0, 2.0, 0.0, 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, 2.0, 0.0],
        [0.0, 0.0, 0.0, 3.0],
    ];
    let x = [1.0; 4];
    let y = matvec(devrik(a), x);
    println!("{:?} {}", y, y == matvec_ref(a, x));
}

Expected output

[3.0, 1.0, 2.0, 3.0] true

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the advantage of column-order storage for this product?

  • Less memory
  • It's easier to write
  • No advantage
  • Each column is contiguous in memory; it fits into a vector with a single load Correct answer

Explanation: Choosing the data layout for the algorithm determines most of the SIMD performance.

2 Vertical multiply-add

Question
for j in 0..4 {
    for i in 0..4 {
        y[i] += a[j][i] * x[____];
    }
}
Answer
for j in 0..4 {
    for i in 0..4 {
        y[i] += a[j][i] * x[j];
    }
}

Explanation: Every lane of column j is multiplied by the same x[j].

3 Approach and property

Term Meaning
Row-dot Needs horizontal reduction
Column axpy Only vertical
Referans uygulama Verification
Devrik Layout conversion

All lessons in Expert