IRCE is an LLVM pass that aims to loop unswitch range checks. As such, its goal is to reduce the performance impact of bounds checks – especially missing @inbounds should no longer prevent vectorization.
To give a simple example:
function sumRange(arr, start, stop)
s = zero(eltype(arr))
for idx = start:stop
s += arr[idx]
end
s
end
It used to be that this checked bounds on every loop iteration, which prevented vectorization and therefore had a huge performance impact (4x+).
Therefore, the old way of writing julia code was:
Base.@propagate_inbounds function sumRange(arr, start, stop)
@boundscheck checkbounds(arr, start:stop)
s = zero(eltype(arr))
@inbounds for idx = start:stop
s += arr[idx]
end
s
end
This is annoying. Enter IRCE, which uses the fact that the loop index and the boundschecks implied by the access are monotone, in order to hoist the bounds-checks out of the loop.
First of all, huge kudos to pchintalapudi who made this work in 2021 (+20/-5 lines over 2 PRs here and here). I had tried the same some years ago, and I failed miserably.
If reliable, then this simple change in the optimization pipeline can change the face of the language.
It seems that this change has flown under the radar for most people. Really, try out how necessary all the @inbounds
are – you might be pleasantly surprised.
That being said, it doesn’t work reliably anymore on master
For example,
function fooSum(arr)
s=zero(eltype(arr))
for i=1:length(arr)
s += arr[i]
end
s
end
used to vectorize in 1.10.1 (it even managed to remove all boundschecks) and doesn’t vectorize any longer in
julia> versioninfo()
Julia Version 1.12.0-DEV.214
Commit 2775c9a0dc4 (2024-03-19 08:20 UTC)