wlav / cppyy

Other
401 stars 41 forks source link

JIT template instantiation with templates loaded by genreflex #6

Open jpedrick opened 3 years ago

jpedrick commented 3 years ago

I would like to be able to instantiate templates loaded/precompiled with genreflex in cppyy similar to how they work with cppyy.cppdef

https://cppyy.readthedocs.io/en/latest/stl.html

I'm not sure what changes would need to be made to genreflex to export template definitions. But, it seems like that project is largely uninterested in JIT template instantiation: https://root-forum.cern.ch/t/genreflex-warning-unused-class-rule/24680

wlav commented 3 years ago

I'm not understanding the question ...

cppyy.cppdef runs Cling's JIT. This is runtime, whereas genreflex generates a C++ file that is subsequently to be compiled and linked into a shared library.

In other words, if you want to include explicit instantiations as part of the genreflex generated C++ code, all you need to do is insert them as text. The compiler will take care of the rest when compiling and linking the generated code.

Now why one would do that, I don't know? If you use genreflex with ROOT (which I don't support) as opposed to with cppyy-cling, then you're not going to get any benefit. The problem is that Apple has implemented templates in the JIT to always instantiate (this allegedly for security reasons, it makes shimming STL classes more difficult; or, per Axel, supposedly "because the C++ standard requires it"), so there is no performance benefit. However, for that reason, there's a patch to Clang in cppyy-cling (which you'll only get if you use it from PyPI, not from conda, as they re-use Clang build by ROOT) where you can declare the explicit instantiation with export in a header to allow by-passing the instantiation in the JIT and instead go directly to the linker to pickup the compiled instantiation.

jpedrick commented 3 years ago

I see, yeah, I don't really know how cppyy works on the backend. What I would like is to cache the "cppyy.cppdef" results, so the next time I run the python script containing that cppdef it loads quickly. So this is really about:

Whether that happens through genreflex or cppyy's JIT doesn't matter too much to me.

wlav commented 3 years ago

That's not currently possible with Clang's current JIT (it may be possible with ORCJit v2). ROOT actually does have some limited options of doing this: a machinery called ACLiC. However, that still suffers from the problem that since it is not doing the linking (the JIT still is), it has to assume that all loaded libraries are dependencies. I.e. the result is typically only usable within the current environment.

Still leaves the question: why not simply compile and link the code with a C++ compiler and then load the shared library? I.e., what makes that it has to be in cppdef? Especially since you mentioned genreflex, which itself is compile-time.

jpedrick commented 3 years ago

Yeah, so the issue is that I manage some legacy code in which templates are perhaps over used. Template parameters are being used for model parameters. My hope was to overcome the need to compile these model parameters ahead of time. So my thought was since cppdef allows the template to be instantiated later on(i.e. my_vec = cppyy.gbl.vector[MyType]() ) that perhaps I could instantiate these template classes at runtime as well.

The templates I'm wanting to override have the form of:

template< int num, int denom > fraction;
template< int buffer_size, typename Fraction > Container;
using container_type1 = Container< 500, fraction<1, 100> >;
template<T> void do_something( );

I would like to be able to call: do_something< Container< 500, fraction<1, 100> > >();

Where the buffer size or fraction can be changed at runtime, like the following:

Container = cppyy.gbl.Container
Fraction = cppyy.gbl.fraction

cppyy.gbl.do_something[ Container[ 500, Fraction[1,100] ] ]()

I hope that clears up my use case.

wlav commented 3 years ago

Yes, that should work as expected ... Doesn't it? The instantiations would be automatic and cppdef isn't involved.

But what it can't do, is save the instantiated templates between sessions: each run, they will be reinstantiated. If this is slow, the only other options are manually building into a shared library or precompiling the headers containing the templates. The latter should work with ROOT/genreflex, but not with cppyy as precompiled modules don't fully work on MS Windows yet

Of course, since the [] of do_something etc. are just Python's __getitem__, you can shim it and collect information on which templates are commonly used. These can then be explicitly instantiated and combined with the export keyword will, with cppyy from PyPI, not reinstantiated on use. See this repo for examples: https://bitbucket.org/kfj/vspline/src/master/ .