JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.74k stars 5.49k forks source link

why does 'in' type-check at run-time? #13048

Closed StephenVavasis closed 9 years ago

StephenVavasis commented 9 years ago

The function 'in' in dict.jl (recent 0.4 master) is written in an unexpected way:

function in(p, a::Associative)
    if !isa(p,Pair)
        error( ETC)
    end
    ETC
end

Why not improve clarity and performance by defining

  function in(p::Pair, a::Associative)

which does the right thing with no run-time error check, and then use

  function in(p::Any, a::Associative)

to catch the bad cases? I'm wondering about this because I am trying to implement 'in' for SortedDict in DataStructures.jl.

jiahao commented 9 years ago

Seems reasonable to me.

jakebolewski commented 9 years ago

The runtime lookup might actually be faster for most common use cases.

jiahao commented 9 years ago

here is a quick and dirty benchmark showing that the multimethods version is significantly faster:

julia> function myin(p, A::Associative)
          if !isa(p, Pair)
            error()
          end
          true
       end
myin (generic function with 1 method)

julia> myin2(p::Pair, A::Associative)=true
myin2 (generic function with 1 method)

julia> myin2(p, A::Associative)=error()
myin2 (generic function with 2 methods)

julia> using Benchmarks #https://github.com/johnmyleswhite/Benchmarks.jl

julia> @benchmark myin(1=>2, Dict())
================ Benchmark Results ========================
     Time per evaluation: 1.45 μs [1.42 μs, 1.49 μs]
...

julia> @benchmark myin2(1=>2, Dict())
================ Benchmark Results ========================
     Time per evaluation: 1.04 μs [1.02 μs, 1.06 μs]
...

julia> @benchmark try myin("X", Dict()) end
================ Benchmark Results ========================
     Time per evaluation: 48.81 μs [47.79 μs, 49.83 μs]
...

julia> @benchmark try myin2("X", Dict()) end
================ Benchmark Results ========================
     Time per evaluation: 48.20 μs [47.05 μs, 49.35 μs]
...
JeffBezanson commented 9 years ago

Yes, we could change this. I think I was the one who wrote this, and my best guess is that it was to avoid possible ambiguities when people define in(x, d::MyDictType).

simonster commented 9 years ago

But to be clear, there is no runtime lookup happening here. The codegen knows what isa is going to return before the code even executes. The only difference between @jiahao's myin and myin2 is that myin emits a useless GC frame for the Pair case (should be fixed by #11508). If the exception to be thrown is defined outside the function, then the LLVM IR is just ret i1 true for both functions when called with a Pair.