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)