开发者

Matching Start / End Profiling Calls

I'm currently implementing a profiling system into an application.

I have a two macro functions which are defined based on a compiler flag (NDEBUG). When NDEBUG is not defined, these two functions (profilingStart / profilingEnd) generate profiling reports which show the time when profilingStart was called, vs the time when profilingEnd was called.

The concern is the potential for a mismatch to occur -- i.e, a scenario when profilingStart has been called, but profilingEnd has not (or vice-versa). My code will already recognize these situations at run-time, but it would be preferable if an error resulted during compile time due to this mismatch.

One suggestion has been to use the do{...}while(); construct to ensure the profiling functions are properly paired. The start macro function would contain the do{ and the end macro would contain the }while(). If one is missing, we would get an error at compile time. However, there are some issues with this -- you could only use the profilingStart() and profilingEnd() calls at the start and end of the function which is being profiled, as using them within the function could impact the scope of local variables (as they may go out of scope due to the do{...}while() call).

Another idea I have had is just to declare a variable in the profilingStart function and then attempt to modify the contents of that variable in the profilingEnd function. This prevents scope issues and woul开发者_如何学Cd generate a compiler error if the variable was never declared. However, I would never have any method of verifying that the contents of the variable are modified in the end function. This only helps with half of the problem, as it does not verify the call of the profilingEnd function.

Any comments are appreciated, as always. Thanks in advance.

EDIT: There may be some confusion regarding my comment(s) regarding scope. profilingStart() and profilingEnd() will always be called within the same function. They may just not be called at the very beginning / very end of the function. Here is an example of what I meant:

int DoSomething(void)
{
   profilingStart();
   int a;
   DoMath(a);
   profilingStop();
   return a; // a is out of scope here, as the do{...}while(0) construct has gone out of scope
}


In C++, one solution is to use the "RAII" idiom. Something like this:

class Profiler {
  public:
    Profiler() { profilingStart(); }
    ~Profiler() { profilingEnd(); }
}

Then you use it like this:

{ // start of block you want to profile
    Profiler prof;
    ...
}

This will ensure that profilingEnd gets called even in the presence of exceptions, early returns, break, etc. That is, it absolutely guarantees the calls are paired.

It does require putting code you want to profile in a block, though.

[edit]

I missed that you want to be able to put profilingEnd in a different block than profilingStart.

See @Roddy's comment below for ideas on how to deal with this; e.g. by having the destructor check to make sure the profiler has been stopped by the time the object is destructed. Although this will not catch the problem at compile time, it will catch it "near" the problem at run time.


Why not just create an object that starts the profile event in the constructor and ends it in the destructor, then using a class similar to scoped_lock, you can ensure that the start is always paired. And seeing as you can create arbitrary scopes, you can do this anywhere


For your question as asked, I recommend @Nemo's answer. Use the ability of C++ to call destructors, and stick to basic lexical scoping.

I hope you're aware that measuring execution time has its own utility, but it's a very indirect way to find "bottlenecks". (I prefer "time drain". Programs aren't slow because they have narrow places, they are slow because they haphazardly do much more than they have to.)

Here's a little more on the issues.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜