0

Some context:

I have a C++ method that performs some intensive computation (some branch-and-bound algorithm on a Vehicle Routing Problem variation). Efficiency is therefore paramount in this code. As I am testing different tricks to achieve optimal speed, I ended up implementing a class StatGatherer that collects information during a given run of the algorithm (namely : how many feasible paths were found, how many were bounded, how many were found unfeasible...). The code looks like this:

void doStuff(const shared_ptr<StatGatherer>& statGatherer = NULL)
{
    //do some stuff
    ...

    if (statGatherer != NULL && some unfeasibility condition)
        statGatherer->countOneFeasiblePath();

    //do more stuff
    ...

    if (statGatherer != NULL && some bounding criterium on the current path)
        statGatherer->countOneBoundedPath();

    //do more stuff
    ...       

    if (statGatherer != NULL && a whole path has been found)
        statGatherer->countOneBoundedPath();
    ...

    //...more information gathering triggered by certain events
}

This works well enough, but ironically, the presence of this kind of 'profiling' code involving statGathererslows down the algorithm quite a bit, as the above pseudo-code is executed tens of millions of times. Even when statGatherer is not provided and defaulted to null, it is still quite a bit slower than not having this code at all.

My question is therefore the following : is there a design that would allow me to achieve the same behavior, but with no loss of efficiency when I do not require to gather statistics, compared to not having this code at all?

Every template solution I can think of still seems to involve some kind of run-time checks like above, so still more time-consuming.

Thanks a lot for your help!

PS: I'm new here so I welcome constructive feedback to make my questions clearer.

GloomyJul
  • 21
  • 3

1 Answers1

1

Template the function on a type that is either StatGatherer or a mock with inline do-nothing implementations (and pass it by reference). The compiler will entirely remove the fake calls in the instantiation with the mock.

An alternative which avoids the need for mock methods but requires the if(statGatherer && ...) statGatherer->...(...); from the original is to mock the pointer: have the template argument be either a StatGatherer* or a dummy pointer type:

template<class T>
struct dummy_ptr {
  operator T*() const {return nullptr;}
  T* operator->() const {return nullptr;}
};
Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • Yes - making decisions a compile time sounds like a good idea. I guess I shouldn't encourage it, but #ifdef and #define also allow you to make decisions at compile time. – mcdowella Oct 11 '17 at 04:31
  • thanks vm David, this seems to do the trick. Only difference with what you describe is that I made by StatGatherer class a data member to avoid passing it around, as it is actually called by subroutines within my main routine. The overhead has indeed practically disappeared when the StatGatherer is a dummy class. I'd also be happy to hear some solution that doesn't require writing dummy code just to make the compiler happy. – GloomyJul Oct 11 '17 at 06:21