CodeBlanks Logo Icon CodeBlanks

Mask

Do conditional packed operations with Mask.

Masks

In SIMD, comparisons produce not a single bool but a mask with one bool per lane. Masks are used with select instead of branching: for each lane, the value is taken either from one vector or the other. This gives conditional processing without if:

// nightly std::simd
let a = i32x4::from_array([1, 5, 3, 8]);
let m = a.simd_gt(i32x4::splat(4));        // [false, true, false, true]
let c = m.select(a, i32x4::splat(0));      // [0, 5, 0, 8]

Branchless code also avoids the processor's branch misprediction penalty on unpredictable conditions.

Masks and selection with arrays

We can write the same idea in stable Rust with arrays; the structure is a branchless form the compiler can vectorize:

fn buyuk_mu(a: [i32; 4], esik: i32) -> [bool; 4] {
    a.map(|x| x > esik)
}

fn sec(m: [bool; 4], a: [i32; 4], b: [i32; 4]) -> [i32; 4] {
    std::array::from_fn(|i| if m[i] { a[i] } else { b[i] })
}

[T; N]::map applies a closure to each element and returns a new array.

Practice Mask and select

Write the buyuk_mu and sec functions above. For a = [1, 5, 3, 8] and threshold 4, print the mask and the result of sec(m, a, [0; 4]) with {:?}, separated by a space.

Starter code

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

Sample solution

main.rs
fn buyuk_mu(a: [i32; 4], esik: i32) -> [bool; 4] {
    a.map(|x| x > esik)
}

fn sec(m: [bool; 4], a: [i32; 4], b: [i32; 4]) -> [i32; 4] {
    std::array::from_fn(|i| if m[i] { a[i] } else { b[i] })
}

fn main() {
    let a = [1, 5, 3, 8];
    let m = buyuk_mu(a, 4);
    println!("{:?} {:?}", m, sec(m, a, [0; 4]));
}

Expected output

[false, true, false, true] [0, 5, 0, 8]

Questions and answers

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

1 Why are masks used instead of <code>if</code> in SIMD?

  • Convention
  • Masks are unnecessary
  • Lanes run the same instruction together; there's no separate branch per lane Correct answer
  • if compiles slowly

Explanation: Both branches' results are computed and the mask picks which to keep.

2 Array map

Question
fn buyuk_mu(a: [i32; 4], esik: i32) -> [bool; 4] {
    a.____(|x| x > esik)
}
Answer
fn buyuk_mu(a: [i32; 4], esik: i32) -> [bool; 4] {
    a.map(|x| x > esik)
}

Explanation: map on an array returns a new array of the same size.

3 Operation and result (a = [1, 5, 3, 8])

Term Meaning
a > 4 [false, true, false, true]
sec(m, a, [0; 4]) [0, 5, 0, 8]
a > 0 [true, true, true, true]
a > 10 [false, false, false, false]

All lessons in Expert