While testing one of my codes for type instability, I stumbled upon what feels like a strange behavior, emphasized by the example below.
julia> test(filepath::String) = @assert ispath(filepath) "File path $filepath is not valid, please verify you input"
test (generic function with 1 method)
julia> test("toto.dat")
ERROR: AssertionError: File path toto.dat is not valid, please verify you input
Stacktrace:
[1] test(filepath::String)
@ Main .\REPL[1]:1
[2] top-level scope
@ REPL[2]:1
julia> @code_warntype test("toto.dat")
MethodInstance for test(::String)
from test(filepath::String) in Main at REPL[1]:1
Arguments
#self#::Core.Const(test)
filepath::String
Body::Nothing
1 β %1 = Main.ispath(filepath)::Bool
βββ goto #3 if not %1
2 β return nothing
3 β %4 = Main.Base::Core.Const(Base)
β %5 = Base.getproperty(%4, :string)::Any
β %6 = Base.string("File path ", filepath, " is not valid, please verify you input")::Any
β %7 = (%5)(%6)::Any
β %8 = Base.AssertionError(%7)::Any
β Base.throw(%8)
βββ Core.Const(:(return %9))
Basically, I did not expect @assert to result in unstable type since among other things AssertionError is a subtype of Exception while @code_warntype returns an Any.
Can somebody clarify this behavior ?
My configuration:
Julia Version 1.7.0-rc2
Commit f23fc0d27a (2021-10-20 12:45 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: 11th Gen Intel(R) Coreβ’ i7-1185G7 @ 3.00GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-12.0.1 (ORCJIT, tigerlake)
Environment:
JULIA_EDITOR = code
JULIA_NUM_THREADS = 6
My understanding is that exceptions only impact performance when they are triggered. Thus the Any you see is not bad. But you should test this with a suitable setup.
Test whether you see bad performance on the code-path which does not hit the exception.
test_assert(filepath::String) = @assert ispath(filepath) "File path $filepath is not valid, please verify you input"
function test_noassert(filepath::String)
if ispath(filepath)
return 1
else
return 2
end
end
then test with @btime from benchmarktools.jl whether the two functions are equally fast when the file exists. This is indeed the case. Thus the Any does not impact performance of the no-throw code-path.
I understand now, thanks for the explanation. However, do you think the Any results from a design choice ? I always thought that this was the last resort in code_warntype, hence my surprise seeing this for a basic type as the AssertionError.
If the line indicated by %1 is false, then the code goes to the section of code indicated by 3, which is the exception being raised. Otherwise, nothing is returned.
Therefore the function is type stable - it either returns nothing or errors.
Iβm not sure why Base.AssertionError has the type of Any, but from a performance point of view this doesnβt matter, as this instability is only ever encountered when raising the exception.