JeffreySarnoff/NamedTupleTools.jl

Avoid splatting and more @inline for better inference

goretkin opened this issue · 2 comments

Here is one example using NamedTupleTools.select

using NamedTupleTools: namedtuple, select

# no inline, no splat (version in NamedTupleTools at time of writing)
select_00(nt::NamedTuple, ks) = namedtuple(ks)(((nt[k] for k in ks)...,))
# inline, no splat
@inline select_10(nt::NamedTuple, ks) = namedtuple(ks)(((nt[k] for k in ks)...,))
# no inline, splat
select_01(nt::NamedTuple, ks) = namedtuple(ks)(map(k->nt[k], ks))
# inline, no splat
@inline select_11(nt::NamedTuple, ks) = namedtuple(ks)(map(k->nt[k], ks))

test_select_hc(t) = (b=t.b, a=t.a)  # hard-coded
test_select_00(t) = select_00(t, (:b, :a))
test_select_01(t) = select_01(t, (:b, :a))
test_select_10(t) = select_10(t, (:b, :a))
test_select_11(t) = select_11(t, (:b, :a))

# @code_llvm test_select_ntt((a=3,b=4,c=5))

using Test: Test

macro testinferred(ex)
  quote
    try
        Test.@inferred $ex
        nothing
    catch e
        e
    end
  end
end

@show @testinferred test_select_00((a=3,b=4,c=5))
@show @testinferred test_select_01((a=3,b=4,c=5))
@show @testinferred test_select_10((a=3,b=4,c=5))
@show @testinferred test_select_11((a=3,b=4,c=5))

produces

@testinferred(test_select_00((a = 3, b = 4, c = 5))) = ErrorException("return type NamedTuple{(:b, :a),Tuple{Int64,Int64}} does not match inferred return type NamedTuple")
@testinferred(test_select_01((a = 3, b = 4, c = 5))) = ErrorException("return type NamedTuple{(:b, :a),Tuple{Int64,Int64}} does not match inferred return type NamedTuple{_A,Tuple{Int64,Int64}} where _A")
@testinferred(test_select_10((a = 3, b = 4, c = 5))) = ErrorException("return type NamedTuple{(:b, :a),Tuple{Int64,Int64}} does not match inferred return type NamedTuple{(:b, :a),_A} where _A<:Tuple")
@testinferred(test_select_11((a = 3, b = 4, c = 5))) = nothing

Also, it's good that test_select_11 and test_select_hc produce the same code (at least on the example I tried).

thanks!