Loops on All Cores
Also known as: parallel multithreading threads concurrency simd auto parallelization openmp rayon performance cores
● Advanced5 minBefore this: For Loop, How Kavach Is Fast
You write the plain loop. The compiler proves it can be split, the runtime runs it on every core, and the answer is the one-core answer, exactly.
for i in 0..16 { out[i] = math.sqrt(xs[i]) }A loop of 16 rounds, each writing out[i] from xs[i]. Written plainly, one core would run round 0, then 1, then 2…
Loops too small to gain stay on one core, and so do sums of floats, whose result depends on the order of adding.
Loops that spread out
Ordinary loops, nothing parallel written anywhere. Element-wise work, where each round writes its own item:
fn main() {
let xs = []
let ys = []
for i in 0..1_000_000 {
xs.push(Float(i % 1000) * 0.125)
ys.push(0.0)
}
for i in 0..1_000_000 {
ys[i] = xs[i] * 2.5 + 1.0
}
print(ys[999_999])
}The program ran and printed:
kavach run elementwise.kav
313.1875A whole-number sum: each core sums its part, and the parts are added at the end, since whole numbers add up the same in any grouping. Overflow is still caught at the plain loop's round:
fn main() {
let xs = []
for i in 0..1_000_000 {
xs.push(i % 100)
}
let total = 0
for i in 0..1_000_000 {
total += xs[i] * 3
}
print(total)
}The program ran and printed:
kavach run sum.kav
148500000Even a running total kept under a modulus: the compiler proves it equals the flat sum taken modulo once.
fn main() {
let total = 0
for i in 0..100_000 {
total = (total + i * 7 % 13) % 1_000_007
}
print(total)
}The program ran and printed:
kavach run chain.kav
599992Loops that stay on one core
fn main() {
let total = 0.0
for _ in 0..1_000 {
total += 0.1
}
print(total)
let last = 0
for i in 0..3 {
last = last * 2 + i
print(last)
}
}The program ran and printed:
kavach run serial.kav
99.9999999999986014
- A sum of floats keeps its order: adding them in another order would change the last digits, so it isn't split.
- Rounds that need the round before can't be split, and neither can a loop that prints.
- Small loops stay on one core too: splitting them would cost more than it saves.
What Kavach does for you
Big loops use every core
A loop over a big list is split across all cores when the result is exactly the one-core result, overflow checks included.
Go deeper the why, for readers who want it
Each chunk runs quietly: where the plain loop would panic, the chunk returns a flag instead. The runtime then throws the chunks' work away and runs the plain loop on one core, which panics at the exact round and line you would have seen. Whole-number sums also record the highest and lowest each chunk's total reached, so a total that would overflow in the middle, in one-core order, is caught too.
loop — 500M rounds of modular arithmetic · WSL Ubuntu 24.04 · i7-13700HX · kavach build (LLVM) · g++ 13 -O2 · Go 1.23 · .NET 8 (C# start-up included) · best of 9
git clone https://github.com/Mithlesh04/kavach && cd kavach
sh scripts/run-benchmarks.sh "" benchmarks/loops