Profiling

by Manu (modified: 2007 Oct 13)

When you have a performance problem, profiling an application will help you figure out where the bottleneck is. However, most profiling tool have a tendency to slow down the execution by a significant factor and EiffelStudio is unfortunately no different. Nevertheless, we have added a neat class called PROFILING_SETTING that will enable you to enable/disable the profiler on only some parts of an execution.

To make sure you are using this class properly you should know the following:

  • Profiling should be enabled in the project settings
  • First executed instruction should be a call to {PROFILING_SETTING}.stop_profiling
  • Last executed instruction should be a call to {PROFILING_SETTING}.start_profiling
  • When profiling a section of code the calls start_profiling and stop_profiling, they should appear in the same routine, as otherwise the profiler will be confused

With EiffelStudio 6.0, there is also a very handy feature that comes for free with the new project settings structure with libraries. By default libraries do not inherit the `profiling' setting and are therefore excluded from the profiling (thus speeding up the profiling). It simply means that you won't know how much time is spent in a routine from a library, but the time is still accounted for the callers of that routine. If you want to profile a library, you simply need to mark it so in your project configuration.

I've showed that you can profile a certain portion of code, but you can also disable profiling a certain portion too. We use it when profiling the compiler for not tracking the time spent in the parser. Here is an example:

f is local l_prof_setting: PROFILING_SETTING l_is_profiling: BOOLEAN retried: BOOLEAN do if not retried then create l_prof_setting.make l_is_profiling := l_prof_setting.is_profiling l_prof_setting.stop_profiling -- Your code here ... end if l_is_profiling then check l_prof_setting_not_void: l_prof_setting /= Void end l_prof_setting.start_profiling end rescue retried := True retry end

Ok, it looks a little bit more complicated than what you expected. If you do not care about correctness then you know that you can remove:

  • the rescue clause if you do not anticipate exceptions
  • the `l_is_profiling' local variable if nothing else is enabling/disabling the profiler

Enjoy Eiffel and profiling!

Comments
  • Peter Gummer (16 years ago 7/10/2007)

    It would look a lot less complicated if Eiffel had try..finally!

    f is local l_prof_setting: PROFILING_SETTING l_is_profiling: BOOLEAN do create l_prof_setting.make try -- WARNING! THIS IS NOT EIFFEL! l_is_profiling := l_prof_setting.is_profiling l_prof_setting.stop_profiling -- Your code here ... finally -- WARNING! THIS IS NOT EIFFEL! if l_is_profiling then l_prof_setting.start_profiling end end end

    • Manu (16 years ago 10/10/2007)

      I've already heard this...

      It would be interesting to have an idea of whom in the Eiffel community favor the existing solution over the more traditional try-catch-finally. Note that you can get the required behavior through inline agents when it is a matter of scoping a few instructions with a rescue clause (agreed it looks a little bit heavy).

      • Colin Adams (16 years ago 10/10/2007)

        No try/catch/finally for me

        I don't care for any of this try (totally redundant noise word)/catch (rescue seems a better word to me)/finally. I didn't think Peter's example looked any more readable - less if anything. Colin Adams

  • Paul Bates (16 years ago 10/10/2007)

    Two Things...

    You should add a shared class SHARED_PROFILING_SETTINGS providing single instance access to the settings.

    Second, it would be nice to have a single routine that starts/stops the profiler, which a client can pass an agent to. The agent is always executed when the profiler polling is suspended.

    • Manu (16 years ago 10/10/2007)

      The agent solution would definitely simplify the code by hiding the rescue clause from the user. I've added something similar for MEMORY in 6.1, the name of the new routine is called execute_without_collection'. In PROFILING_SETTINGS, we could have execute_without_profiling'.

    • Peter Gummer (16 years ago 12/10/2007)

      Definitely add the agent

      The SHARED_PROFILE_SETTINGS idea is good. It would make it clear that this is a singleton setting. The current approach of creating a new object, which you then instruct to start and stop, is a bit weird. It feels like you are starting and stopping something local, but you have to imagine that the implementation is starting and stopping some global flag.

      The agent is a great idea. I've used a similar pattern to ensure, for example, that an EiffelVision application's hourglass cursor gets reset to the the normal mouse cursor even if there's an exception. I've noticed that Eiffel programmers tend to be lax at this, compared with good programmers in languages with try..finally at their disposal; partly because Eiffel's rescue mechanism is more heavy-handed in these situations; and partly because the Eiffel philosophy that exceptions are bugs tends to cause us to pretend that they will never happen. (I learned long ago that bugs often occur when you least expect them. That's what attracted me to DbC in the first place. It also made me paranoid about ensuring that resources are always disposed or reset, even in the event of an exception.)

      Yet another example of the power of Eiffel idioms! I can't understand Colin's opinion that the try..finally code looks as complicated as Manu's rescue code, but Paul has shown a third way. I'm more than happy with this idiom. I just wonder sometimes, however, whether the need to relearn stuff like this just raises too much of a learning barrier for those zillions of programmers out there who, like me, are comfortable with the try..finally idiom. Maybe someone will invent an even simpler way someday, that hides the rescue and the agent behind some syntactic sugar: maybe something like C#'s using?