Avoid runtime dispatch when overloading `isless()`

I have been learning to use JET.jl to improve the performance of my code. I am still getting familiar with its output. I would appreciate some help understanding why the function below produces a runtime dispatch warning in @report_opt.

Basically, what we do is define a struct Rectangle, overload isless() to sort rectangles by their area, then create a function that generates m random rectangles and finds the largest one and its index.

julia> import Base.isless

julia> using JET

julia> struct Rectangle

julia> isless(r1::Rectangle, r2::Rectangle) = isless(r1.width * r1.height, r2.width * r2.height)
isless (generic function with 44 methods)

julia> function largestrandomrectangle(m::Int)
           rects = Rectangle[Rectangle(rand(), rand()) for _ in 1:m]
largestrandomrectangle (generic function with 1 method)

julia> @report_opt largestrandomrectangle(100)
═════ 2 possible errors found ═════
β”Œ @ REPL[5]:2 Main.rand()
β”‚β”Œ @ /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.8/Random/src/Random.jl:257 #self#(Random.default_rng(), Random.Float64)
β”‚β”‚β”Œ @ /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.8/Random/src/Random.jl:257 Random.Val(1)
β”‚β”‚β”‚β”Œ @ essentials.jl:714 %1()
β”‚β”‚β”‚β”‚ runtime dispatch detected: %1::Type{Val{_A}} where _A()
β”Œ @ REPL[5]:4 Main.findmax(rects)
β”‚β”Œ @ reducedim.jl:1159 Base.#findmax#821(Base.:, #self#, A)
β”‚β”‚β”Œ @ reducedim.jl:1159 Base._findmax(A, dims)
β”‚β”‚β”‚β”Œ @ reduce.jl:911 Base.findmax(Base.identity, a)
β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:885 Base.mapfoldl(#275, Base._rf_findmax, Base.pairs(domain))
β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:162 Base.#mapfoldl#257(Base._InitialValue(), #self#, f, op, itr)
β”‚β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:162 Base.mapfoldl_impl(f, op, init, itr)
β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:44 Base.foldl_impl(opβ€², nt, itrβ€²)
β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:49 Base.reduce_empty_iter(op, itr)
β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:370 Base.reduce_empty_iter(op, itr, Base.IteratorEltype(itr))
β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:371 Base.reduce_empty(op, Base.eltype(itr))
β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”Œ @ reduce.jl:348 Base.mapreduce_empty(#275, $(QuoteNode(Base.BottomRF{typeof(Base._rf_findmax)}(Base._rf_findmax))), _)
β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚ runtime dispatch detected: Base.mapreduce_empty(#275, $(QuoteNode(Base.BottomRF{typeof(Base._rf_findmax)}(Base._rf_findmax)))::Base.BottomRF{typeof(Base._rf_findmax)}, _::Type{Pair{Int64, Rectangle}})

The first issue shows up even if you call @report_opt rand(), so I am not sure I can do anything about it.

The second issue is what confuses me. I think what it is saying is that my function will error if the rects is empty (i.e. if m=0). But that is exactly what I want to happen!

julia> findmax(Rectangle[])
ERROR: MethodError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer

Am I safe to ignore the warning, or will I actually obtain better performance by modifying largestrandomrectangle() to return some kind of dummy object for empty input?

Looking at the source code of findmax

it does this

findmax(itr) = _findmax(itr, :)
_findmax(a, ::Colon) = findmax(identity, a)

and the documentation for the call using identity says

domain must be a non-empty iterable.

So I guess the documentation for findmax(itr) should also mention this condition.

It is the caller’s responsibility to check for valid arguments, so how you handle that is up to you.

The maximum area of no rectangles is …

off topic

the minimum area of no rectangles

1 Like