> But that is not the relevant pattern. What we require is the *joint*
> instantiation of A B and C types, and a dispatch that is specialized for
> this joint combination, and hence is as fast as possible.

The joint combination is straightforward:

template<class A, class B, class C>
void fooImpl(A a, B b, C c);

void foo(std::variant<A1..> avar,
         std::variant<B1..> bvar,
         std::variant<C1..> cvar)
{
    std::visit(
        [](auto a, auto b, auto c)
        {
            fooImpl(a, b, c);
        },
        avar, bvar, cvar);
}

but in this case, as you pointed out, we don't get much compile time
advantage - but we do still enjoy the fast dispatch.

> What you are describing (independent dispatching for every type) is not
> very different from dynamic typing, unless the code can be divided into
> clear disjoint blocks as in your example, which is not the case for most
> algorithms in graph-tool

I will defer to your judgment on that one, though perhaps surprisingly I
found this worked in the first (only) algorithm I tried applying it to: assortativity.
I selected it based on how long it took to build. The results were:

boost::any + typelist : 177s, 4.5GB memory
std::variant for edge weights only: 37s + 1.74GB

The memory reduction is very useful in that it enables parallel builds.

The prototype can be found here: https://git.skewed.de/jaafar/graph-tool/compare/master...feature%2Fvariant-conversion-demo

Best,
Jeff


On Thu, Feb 6, 2020 at 1:18 PM Tiago de Paula Peixoto <tiago@skewed.de> wrote:
Am 06.02.20 um 18:59 schrieb Jeff Trull:
>> std::variant. The high compile times stem simply from the fact we have
>> to cycle through the Cartesian product of the set of types of each
>
> I absolutely agree on that diagnosis, but I think std::variant can play
> a role. The key
> is to postpone dispatch, where possible, to refactor out one or more of
> the product
> terms.
>
> At the moment the dispatch mechanism identifies all the concrete types
> first, /then/
> runs the correct instantiated function. If there were more flexibility
> in this process,
> dispatching to selected type-dependent code could happen later and cover
> less code.
> Consider:
>
> template<class A, class B, class C>
> void foo(A a, B b, C c)
> {
>     // lots of A and B code
>     // a single use of C
>     // lots more A and B
> }
>
> For the sake of a small amount of code involving C the entire function
> gets rebuilt as
> many times as there are C types. Now consider an approach that postponed
> determining C's concrete type:
>
> template<class A, class B>
> void foo(A a, B b, std::variant<C1, C2, ...> c_var)
> {
>     // lots of A and B
>     std::visit([](auto const & c){ // use of C }, c_var);
>     // lots more A and B
> }
>
> If C was something based on scalar_types this would mean a factor of 6
> reduction in
> instantiations!

But that is not the relevant pattern. What we require is the *joint*
instantiation of A B and C types, and a dispatch that is specialized for
this joint combination, and hence is as fast as possible.

What you are describing (independent dispatching for every type) is not
very different from dynamic typing, unless the code can be divided into
clear disjoint blocks as in your example, which is not the case for most
algorithms in graph-tool.

Best,
Tiago

--
Tiago de Paula Peixoto <tiago@skewed.de>
_______________________________________________
graph-tool mailing list
graph-tool@skewed.de
https://lists.skewed.de/mailman/listinfo/graph-tool