samedi 27 juillet 2019

Avoiding spurious construction and destruction in RAII timer object

I have a Timer object which should time the region of code from its construction to its destruction. These Timer objects are created by and associated with a long-lived TimerMananger object. In fact, the Timer objects are just thin wrappers around a pointer to TimerManager which does the heavy lifting.

I'd like the user the Timer objects like this:

TimerManager manager; // maybe a global, thread local, whatever

...
{
  Timer timer = manager.newTimer();
  // code under test

} // timer object destroyed and timer stops here

The Timer object can be as simple as:

class Timer {
  TimerManger* manager_;
public:
  Timer(TimerManger* manager) : manager_(manager) {
    manager_->start();
  }

  ~Timer() {
    manager_->stop();
  }
};

Here all the heavy lifting of starting and stopping the timer is delegated to the manager.

However, if I implement TimerManager::newTimer() like so:

TimerManager::newTimer() {
  Timer t(this);
  // ...
  return t;
}

Then depending on whether RVO kicks in, I may get a spurious construction and destruction of Timer object t, different from the real region I want to time in the calling code.

I could instead use the following code to initialize Timer objects:

{
  Timer timer(&manager);
  // code under test

} // timer object destroyed and timer stops here

This ensures extra Timer objects are not created or destroyed, but I prefer the assignment syntax, especially since it lets me have various newTimer() type methods with different behavior. Is there any way to get something like this w/o having the extra side effects of Timer creation and destruction.

Performance matters here.

I am not using C++17 so I cannot avail myself of guaranteed return value optimization.

Aucun commentaire:

Enregistrer un commentaire