Skip to content

Remove literals from generated functor code

Implements #313

  • augment the (python) grammar of functors to keep track of literal arguments (recursively)
  • remove any literal from the generated C++ code, instead generate plugin factory code instances which take a std::vector<std::string> as argument, wrapped inside a dedicate Functor::Arguments class, one element for each literal argument
  • when requesting a functor at runtime, pass the std::string representations of the literals as arguments to the factory's create method using the above mentioned Functors::Arguments wrapper as 'erased' type.
  • inside the factory function, when requesting literals to instantiate the correct functor instance, Functor::Arguments converts ('un-erases') the provided std::string into the specified argument type using Gaudi::Parsers::parse, making the literals available as arguments of the type needed.

This significantly reduces the amount of code to be compiled in the functor cache, especially in cases where the same functor is used many times, but each time with eg. different TES locations, or cut values. Note that the reduction factor will strongly depend on the (pattern of) functors being requested.

An example may clarify what actually happens. Previously, the generated code would look like:


// ( ( SIZE_OF @ _TES(DataHandles=[DataHandle('/Event/VDP/OutputLocation')], DataTypes=['std::vector<int,std::allocator<int> >']) ) < 5 )
std::unique_ptr<Functors::AnyFunctor> factory_0x67210a951f803ccf(){
  return std::unique_ptr<Functors::AnyFunctor>{new Functors::Functor<bool ()>(
      operator<( Functors::chain( ::Functors::detail::SizeOf{}, 
                                  ::Functors::Common::TES<std::vector<int,std::allocator<int> >>( std::vector<std::string>{std::string{"/Event/VDP/OutputLocation"}} ) ),
                 int{5} )
      )
  };
}
::Gaudi::PluginService::DeclareFactory<Functors::AnyFunctor, ::Gaudi::PluginService::Factory<Functors::AnyFunctor*()>>
functor_0x67210a951f803ccf{
  std::string{"0x67210a951f803ccf"},
  factory_0x67210a951f803ccf
};

but with this MR, it instead will be:

::Gaudi::PluginService::DeclareFactory<Functors::AnyFunctor, ::Gaudi::PluginService::Factory<Functors::AnyFunctor*(Functors::Arguments const&)>>
functor_0x743388e24224538e{
  std::string{"0x743388e24224538e"},
  []( [[maybe_unused]] Functors::Arguments const& args ) -> std::unique_ptr< Functors::AnyFunctor> {
      return std::make_unique<Functors::Functor<bool ()>>(
          operator<( Functors::chain( ::Functors::detail::SizeOf{}, 
                                      ::Functors::Common::TES<std::vector<int,std::allocator<int> >>( /* List of DataHandles */ static_cast<std::vector<std::string>>( args[0] ) ) ), 
                     static_cast<int>( args[1] ) 
                   )
      );
  }
};

This reduces the number of factory functions by eliminating the need to 'hardwire' the literals in the C++ code. Specifically, running grep DeclareFactory **/*cpp | wc in MooreCache shows that the number of factory entries is reduced from 44332 to 8495, i.e. by about 5x. Note however that this comes at a cost of the addition of instances of parse(Arg& arg, std::string const&) functions which convert strings to the required literals (but the number of such functions is determined by the number of different argument types used, which is 'small')

Edited by Gerhard Raven

Merge request reports