Here’s an interesting related example. Suppose we already have a tuple order
that contains a topological sort of singleton structs, and we have a smaller tuple xs
with a subset of the full topological sort (with no duplicates). However, the elements in xs
are not topologically sorted. What we would like to do is return a new tuple where the elements of xs
are topologically sorted. We can do this with the following recursive code.
Implementation:
function in_tuple(x::S, t::Tuple{T, Vararg}) where {S, T}
S === T || in_tuple(x, Base.tail(t))
end
in_tuple(::T, t::Tuple{T, Vararg}) where {T} = true
in_tuple(_, t::Tuple{}) = false
Base.@assume_effects :total function tuple_topo_sort(order, match, xs)
if length(match) == length(xs)
return match
end
first = order[1]
if in_tuple(first, xs)
tuple_topo_sort(Base.tail(order), (match..., first), xs)
else
tuple_topo_sort(Base.tail(order), match, xs)
end
end
Test case:
using Test
using BenchmarkTools
for i in 1:100
sym = Symbol("A$i")
@eval struct $sym end
end
order_vec = map(1:100) do i
sym = Symbol("A$i")
eval(:($sym()))
end
order = tuple(order_vec...)
The tuple_topo_sort
function is inferred for this example:
julia> @inferred tuple_topo_sort(order, (), (A75(), A50(), A25()))
(A25(), A50(), A75())
However, it does not completely compile away:
julia> @code_typed tuple_topo_sort(order, (), (A75(), A50(), A25()))
CodeInfo(
1 ─ nothing::Nothing
│ nothing::Nothing
│ Core._apply_iterate(Base.iterate, Base.argtail, order)::Tuple{A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, A100}
└── return (A25(), A50(), A75())
) => Tuple{A25, A50, A75}
We can see a Core._apply_iterate
in there, and we can see its affect on the runtime of tuple_topo_sort
:
julia> @btime tuple_topo_sort(order, (), (A75(), A50(), A25()));
3.030 μs (0 allocations: 0 bytes)
Any ideas on how to get this case to completely compile away?