Skip to content
Kavachकवच

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]) }
one core
0123456789101112131415
step 1 of 5

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.1875
elementwise.kav: Kavach code, then what running it prints

A 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

148500000
sum.kav: Kavach code, then what running it prints

Even 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

599992
chain.kav: Kavach code, then what running it prints

Loops 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
serial.kav: Kavach code, then what running it prints
  • 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.

    decision 0031

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

Kavach60 ms · finished 1st
C++ -O21,361 ms · 23× behind
Go1,377 ms · 23× behind
C#1,561 ms · 26× behind
Verify itgit clone https://github.com/Mithlesh04/kavach && cd kavach sh scripts/run-benchmarks.sh "" benchmarks/loops

Check yourself

QUIZ 1 / 1pick one
A chunk running on another core hits an overflow. What does the program print?