GEOS-DEV / GEOS-DEV/GEOS

Semi-Automated explicit instantiation of templated functions in separate compilation units

Open
#1,994 11 comments 0 reactions 2 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
287
Forks
109
Avg merge
4d 41m
Merged PRs (30d)
5

Description

What is the requested feature?
We have various functions that get called after going through a series of "pass thru" template, which are essentially a large manual if/elseif/.../else block (edit...not true any longer. It is now a recursive function call with an if check to see if if the cast succeeded...see ConstitutivePassThruHandler below ) for calling compile time instantiations of functions from a runtime interface. This is all well and good, except that it results in huge compilation units that compile all permutations of the pass-thru. This has two problems: 1) long compile times that are not able to use parallelism, 2) large memory requirements for these bloated compilation units.

example:

template< typename LAMBDA >
void constitutiveUpdatePassThru( MultiFluidBase const & fluid,
                                 LAMBDA && lambda )
{
  ConstitutivePassThruHandler< DeadOilFluid,
                               BlackOilFluid,
#ifdef GEOSX_USE_PVTPackage
                               CompositionalMultiphaseFluid,
#endif
                               CO2BrinePhillipsFluid,
                               CO2BrineEzrokhiFluid,
                               CO2BrinePhillipsThermalFluid /*, // if I uncomment the two models at the same time, the compiler segfaults on
                                                               Lassen!
                                                               CO2BrineEzrokhiThermalFluid*/>::execute( fluid, std::forward< LAMBDA >( lambda ) );
}

and

template< typename TYPE, typename ... TYPES >
struct ConstitutivePassThruHandler< TYPE, TYPES... >
{
  template< typename BASE, typename LAMBDA >
  static void execute( BASE & relation, LAMBDA && lambda )
  {
    using Derived = add_const_if_t< TYPE, std::is_const< BASE >::value >;

    if( dynamicCast< Derived * >( &relation ) )
    {
      lambda( static_cast< Derived & >( relation ) );
    }
    else
    {
      ConstitutivePassThruHandler< TYPES... >::execute( relation, std::forward< LAMBDA >( lambda ) );
    }
  }
};

Ideally, the lambda function will call another function that is templated on the strong type of fluid that is passed to execute, which is passed to lambda eventually. For instance, `PVTDriver.cpp has :

  constitutiveUpdatePassThru( baseFluid, [&] ( auto & selectedFluid )
  {
    using FLUID_TYPE = TYPEOFREF( selectedFluid );
    runTest< FLUID_TYPE >( selectedFluid, m_table );
  } );

We need to have cmake create a bunch of compilation units that look like:

#include "PVTDriverRunTest.hpp"
#include "CompositionalMultiphaseFluid.hpp"
namespace geosx
{
template void PVTDriver::runTest< constitutive::CompositionalMultiphaseFluid >( constitutive::CompositionalMultiphaseFluid &, arrayView2d< real64 > const & );
}

Is your request related to a specific problem?
Long compilation times,
Large memory footprints
failing compilation on CUDA machines

Describe the solution you'd like
We can make a semi-automated process to generate compilation units for each instantiation. The proposed process is as follows:

  1. We take the templated function/s and place them in either a single header, or a collection of stand-alone headers. In the example above, this is the contents of PVTDriverRunTest.hpp.
  2. We creates a cmake list of classnames/filenames that contain the various template parameters in the pass-thru function
  3. We create a "template file" that is the pattern for the compilation units.
  4. We use cmake to use substitution and generate the compilation units....of course we add the generated compilation units to the list of sources for the module we are in.

Describe alternatives you've considered
JITC

When assigned, developer should coordinate with @klevzoff and @rrsettgast

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.