Mask
- Rust course
- Expert
- Lesson 21/46
- 4 min
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
fn main() {
// buraya yaz
}Sample solution
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
fn buyuk_mu(a: [i32; 4], esik: i32) -> [bool; 4] {
a.____(|x| x > esik)
}
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] |