Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Catalyst: Dev

Proposal for built-in profiling function

 

 

Catalyst dev RSS feed   Index | Next | Previous | View Threaded


jon+catalyst at youramigo

Oct 11, 2007, 5:47 AM

Post #1 of 10 (2475 views)
Permalink
Proposal for built-in profiling function

That Catalyst reports the amount of time taken in each component method
is very neat... but restrictive. It would be nice to easily add
"profile points" into the code and have the execution time to those
points reported as well.

The idea is that the summary report which currently looks something like
this:

.----------------------------------------------------------------+-----------.
| Action | Time |
+----------------------------------------------------------------+-----------+
| /status/auto | 0.000751s |
| /status/syncstatus | 0.241494s |
| /end | 0.065917s |
| -> SLx::View::TT->process | 0.064111s |
'----------------------------------------------------------------+-----------'

with scatterings of code like this:

$c->profile("starting long routine");
...
$c->profile("done stuff");
...

might turn the resulting report into this:

.----------------------------------------------------------------+-----------.
| Action | Time |
+----------------------------------------------------------------+-----------+
| /status/auto | 0.000751s |
| /status/syncstatus | 0.241494s |
| starting long routine | 0.000213s |
| done stuff | 0.221234s |
| /end | 0.065917s |
| -> SLx::View::TT->process | 0.064111s |
'----------------------------------------------------------------+-----------'

where each reported time for a profile point is the time since the
previous profile point or the beginning of the execution of the
component.

I tried using Devel::Profile and a couple similar modules, but Catalyst
died (in View::TT) on startup. The reason for this wasn't clear, but I
didn't try that hard to work it out because that type of profiler gives
me timings to a subroutine level, but I really wanted to put profile
points through arbitrary bits of code with ease.
C::P::DBIC::Schema::Profiler is also useful, but only for DBIx::Class
models.

So I started writing a plugin to do this (yes, a plugin, because it
needed the request start time), but realised that I would have to
either:

- have a profiling mechanism separate from the core, which would have to
duplicate functionality already in the core, or

- append to the mechanism in the core, requiring that the plugin mess
with internals of the core

... neither of which were entirely satisfactory.

So, I'd like to propose a trivial change to the core code to allow this
functionality. Basically it just adds a 'profile' method which hooks
into the existing timing tree. I've been running this code and found it
enormously useful in isolating problem areas. Patch for Catalyst.pm
against 5.7010 is attached.

--

Jon
Attachments: profile-patch.txt (2.72 KB)


dbix-class at trout

Oct 11, 2007, 2:39 PM

Post #2 of 10 (2331 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

On Thu, Oct 11, 2007 at 10:17:13PM +0930, Jon Schutz wrote:
> That Catalyst reports the amount of time taken in each component method
> is very neat... but restrictive. It would be nice to easily add
> "profile points" into the code and have the execution time to those
> points reported as well.
>
> The idea is that the summary report which currently looks something like
> this:
>
> .----------------------------------------------------------------+-----------.
> | Action | Time |
> +----------------------------------------------------------------+-----------+
> | /status/auto | 0.000751s |
> | /status/syncstatus | 0.241494s |
> | /end | 0.065917s |
> | -> SLx::View::TT->process | 0.064111s |
> '----------------------------------------------------------------+-----------'
>
> with scatterings of code like this:
>
> $c->profile("starting long routine");
> ...
> $c->profile("done stuff");
> ...

Wouldn't it be better to have start_profile + end_profile methods?

There was a Catalyst::Plugin::Timer at one point proposed that did roughly
that.

> So, I'd like to propose a trivial change to the core code to allow this
> functionality. Basically it just adds a 'profile' method which hooks
> into the existing timing tree. I've been running this code and found it
> enormously useful in isolating problem areas. Patch for Catalyst.pm
> against 5.7010 is attached.

Your patch isn't whitespace clean (you have whitespace only changes and
mixed spaces and tabs in there), has no tests and isn't a diff -ur, so I can't
actually tell whether it does anything sensible.

Try again please?

(Oh, and you want Catalyst::Model::DBIC::Schema::QueryLog - avoid the DBIC
profiling plugins, they're all variously broken)

--
Matt S Trout Need help with your Catalyst or DBIx::Class project?
Technical Director http://www.shadowcat.co.uk/catalyst/
Shadowcat Systems Ltd. Want a managed development or deployment platform?
http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/servers/

_______________________________________________
Catalyst-dev mailing list
Catalyst-dev [at] lists
http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst-dev


jon+catalyst at youramigo

Oct 11, 2007, 7:21 PM

Post #3 of 10 (2332 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

On Thu, 2007-10-11 at 22:39 +0100, Matt S Trout wrote:
> On Thu, Oct 11, 2007 at 10:17:13PM +0930, Jon Schutz wrote:
> > That Catalyst reports the amount of time taken in each component method
> > is very neat... but restrictive. It would be nice to easily add
> > "profile points" into the code and have the execution time to those
> > points reported as well.
> >
> > The idea is that the summary report which currently looks something like
> > this:
> >
> > .----------------------------------------------------------------+-----------.
> > | Action | Time |
> > +----------------------------------------------------------------+-----------+
> > | /status/auto | 0.000751s |
> > | /status/syncstatus | 0.241494s |
> > | /end | 0.065917s |
> > | -> SLx::View::TT->process | 0.064111s |
> > '----------------------------------------------------------------+-----------'
> >
> > with scatterings of code like this:
> >
> > $c->profile("starting long routine");
> > ...
> > $c->profile("done stuff");
> > ...
>
> Wouldn't it be better to have start_profile + end_profile methods?
>
> There was a Catalyst::Plugin::Timer at one point proposed that did roughly
> that.
>

I don't know what anyone else thinks, but IMHO having a single method
halves the number of lines of code for typical use cases. e.g. starting
with a big block of code where you just want to tag, say, 10 points and
see the timings, that's 10 calls to $c->profile(). With
start/end_profile, you'd need to add them in pairs, making 20 calls. In
the other extreme, where you just want to get the timing of one section
of code, it's 2 calls either way.

> > So, I'd like to propose a trivial change to the core code to allow this
> > functionality. Basically it just adds a 'profile' method which hooks
> > into the existing timing tree. I've been running this code and found it
> > enormously useful in isolating problem areas. Patch for Catalyst.pm
> > against 5.7010 is attached.
>
> Your patch isn't whitespace clean (you have whitespace only changes and
> mixed spaces and tabs in there), has no tests and isn't a diff -ur, so I can't
> actually tell whether it does anything sensible.
>
> Try again please?
>

Sure. Perhaps you could point me in the right direction w.r.t. the
tests. I couldn't see any existing test for the component timings that
I could add to. Is there a representative test case there that captures
STDERR or log output and verifies its content, that I can use as a base?

> (Oh, and you want Catalyst::Model::DBIC::Schema::QueryLog - avoid the DBIC
> profiling plugins, they're all variously broken)
>

Ta.

--

Jon


_______________________________________________
Catalyst-dev mailing list
Catalyst-dev [at] lists
http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst-dev


dbix-class at trout

Oct 12, 2007, 10:45 AM

Post #4 of 10 (2324 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

On Fri, Oct 12, 2007 at 11:51:12AM +0930, Jon Schutz wrote:
> On Thu, 2007-10-11 at 22:39 +0100, Matt S Trout wrote:
> > On Thu, Oct 11, 2007 at 10:17:13PM +0930, Jon Schutz wrote:
> > > That Catalyst reports the amount of time taken in each component method
> > > is very neat... but restrictive. It would be nice to easily add
> > > "profile points" into the code and have the execution time to those
> > > points reported as well.
> > >
> > > The idea is that the summary report which currently looks something like
> > > this:
> > >
> > > .----------------------------------------------------------------+-----------.
> > > | Action | Time |
> > > +----------------------------------------------------------------+-----------+
> > > | /status/auto | 0.000751s |
> > > | /status/syncstatus | 0.241494s |
> > > | /end | 0.065917s |
> > > | -> SLx::View::TT->process | 0.064111s |
> > > '----------------------------------------------------------------+-----------'
> > >
> > > with scatterings of code like this:
> > >
> > > $c->profile("starting long routine");
> > > ...
> > > $c->profile("done stuff");
> > > ...
> >
> > Wouldn't it be better to have start_profile + end_profile methods?
> >
> > There was a Catalyst::Plugin::Timer at one point proposed that did roughly
> > that.
> >
>
> I don't know what anyone else thinks, but IMHO having a single method
> halves the number of lines of code for typical use cases. e.g. starting
> with a big block of code where you just want to tag, say, 10 points and
> see the timings, that's 10 calls to $c->profile(). With
> start/end_profile, you'd need to add them in pairs, making 20 calls. In
> the other extreme, where you just want to get the timing of one section
> of code, it's 2 calls either way.

Having a single method also makes it impossible to get nested graphs like we
currently provide for forward() though.

> > > So, I'd like to propose a trivial change to the core code to allow this
> > > functionality. Basically it just adds a 'profile' method which hooks
> > > into the existing timing tree. I've been running this code and found it
> > > enormously useful in isolating problem areas. Patch for Catalyst.pm
> > > against 5.7010 is attached.
> >
> > Your patch isn't whitespace clean (you have whitespace only changes and
> > mixed spaces and tabs in there), has no tests and isn't a diff -ur, so I can't
> > actually tell whether it does anything sensible.
> >
> > Try again please?
> >
>
> Sure. Perhaps you could point me in the right direction w.r.t. the
> tests. I couldn't see any existing test for the component timings that
> I could add to. Is there a representative test case there that captures
> STDERR or log output and verifies its content, that I can use as a base?

I'd write something that dug into $c->stats at request-end.

I do wonder if with a bit of work you couldn't make $c->stats a subclass of
a tree and provide an API that -any- profiling code could attach itself to
without needing a plugin. Adding methods to core still feels like it's
unnecessary though I've not poked around in detail as to alternatives.

--
Matt S Trout Need help with your Catalyst or DBIx::Class project?
Technical Director http://www.shadowcat.co.uk/catalyst/
Shadowcat Systems Ltd. Want a managed development or deployment platform?
http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/servers/

_______________________________________________
Catalyst-dev mailing list
Catalyst-dev [at] lists
http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst-dev


jon+catalyst at youramigo

Oct 15, 2007, 6:34 PM

Post #5 of 10 (2318 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

On Fri, 2007-10-12 at 18:45 +0100, Matt S Trout wrote:
> On Fri, Oct 12, 2007 at 11:51:12AM +0930, Jon Schutz wrote:
> > On Thu, 2007-10-11 at 22:39 +0100, Matt S Trout wrote:
> > > On Thu, Oct 11, 2007 at 10:17:13PM +0930, Jon Schutz wrote:
> > > > That Catalyst reports the amount of time taken in each component method
> > > > is very neat... but restrictive. It would be nice to easily add
> > > > "profile points" into the code and have the execution time to those
> > > > points reported as well.
> > > >
> > > > The idea is that the summary report which currently looks something like
> > > > this:
> > > >
> > > > .----------------------------------------------------------------+-----------.
> > > > | Action | Time |
> > > > +----------------------------------------------------------------+-----------+
> > > > | /status/auto | 0.000751s |
> > > > | /status/syncstatus | 0.241494s |
> > > > | /end | 0.065917s |
> > > > | -> SLx::View::TT->process | 0.064111s |
> > > > '----------------------------------------------------------------+-----------'
> > > >
> > > > with scatterings of code like this:
> > > >
> > > > $c->profile("starting long routine");
> > > > ...
> > > > $c->profile("done stuff");
> > > > ...
> > >
> > > Wouldn't it be better to have start_profile + end_profile methods?
> > >
> > > There was a Catalyst::Plugin::Timer at one point proposed that did roughly
> > > that.
> > >
> >
> > I don't know what anyone else thinks, but IMHO having a single method
> > halves the number of lines of code for typical use cases. e.g. starting
> > with a big block of code where you just want to tag, say, 10 points and
> > see the timings, that's 10 calls to $c->profile(). With
> > start/end_profile, you'd need to add them in pairs, making 20 calls. In
> > the other extreme, where you just want to get the timing of one section
> > of code, it's 2 calls either way.
>
> Having a single method also makes it impossible to get nested graphs like we
> currently provide for forward() though.
>
> > > > So, I'd like to propose a trivial change to the core code to allow this
> > > > functionality. Basically it just adds a 'profile' method which hooks
> > > > into the existing timing tree. I've been running this code and found it
> > > > enormously useful in isolating problem areas. Patch for Catalyst.pm
> > > > against 5.7010 is attached.
> > >
> > > Your patch isn't whitespace clean (you have whitespace only changes and
> > > mixed spaces and tabs in there), has no tests and isn't a diff -ur, so I can't
> > > actually tell whether it does anything sensible.
> > >
> > > Try again please?
> > >
> >
> > Sure. Perhaps you could point me in the right direction w.r.t. the
> > tests. I couldn't see any existing test for the component timings that
> > I could add to. Is there a representative test case there that captures
> > STDERR or log output and verifies its content, that I can use as a base?
>
> I'd write something that dug into $c->stats at request-end.
>
> I do wonder if with a bit of work you couldn't make $c->stats a subclass of
> a tree and provide an API that -any- profiling code could attach itself to
> without needing a plugin. Adding methods to core still feels like it's
> unnecessary though I've not poked around in detail as to alternatives.
>

I've created Catalyst::Stats and factored out the embedded stats code in
Catalyst.pm. Default functionality is identical to existing. Anybody
can substitute their own class with
__PACKAGE__->stats_class("My::Stats"). $c->stats returns the stats
object. Thus typical use is $c->stats->profile(...). The profile
method can take a single comment or multiple args to mark begin/end so
fits both use cases discussed above.

Importantly, stats can be enabled without having to enable debug,
through the normal mechanisms - use Catalyst qw/-Stats=1/,
CATALYST_STATS=1. When disabled, overhead is negligible.

Patch against Catalyst-Runtime-5.7010 is attached, inc doc and tests.
Perldoc provided below for convenience.

Regards,

--

Jon

==================================

NAME
Catalyst::Stats - Catalyst Timing Statistics Class

SYNOPSIS
$stats = $c->stats;
$stats->enable(1);
$stats->profile($comment);
$stats->profile(begin => $block_name, comment =>$comment);
$stats->profile(end => $block_name);
$elapsed = $stats->elapsed;
$report = $stats->report;

See Catalyst.

DESCRIPTION
This module provides the default, simple timing stats collection functionality for
Catalyst. If you want something different set "MyApp->stats_class" in your applica-
tion module, e.g.:

__PACKAGE__->stats_class( "My::Stats" );

If you write your own, your stats object is expected to provide the interface
described here.

Catalyst uses this class to report timings of component actions. You can add profil-
ing points into your own code to get deeper insight. Typical usage might be like this:

sub mysub {
my ($c, ...) = @_;
$c->stats->profile(begin => "mysub");
# code goes here
...
$c->stats->profile("starting critical bit");
# code here too
...
$c->stats->profile("completed first part of critical bit");
# more code
...
$c->stats->profile("completed second part of critical bit");
# more code
...
$c->stats->profile(end => "mysub");
}

Supposing mysub was called from the action "process" inside a Catalyst Controller
called "service", then the reported timings for the above example might look something
like this:

.----------------------------------------------------------------+-----------.
│ Action │ Time │
+----------------------------------------------------------------+-----------+
│ /service/process │ 1.327702s │
│ mysub │ 0.555555s │
│ - starting critical bit │ 0.111111s │
│ - completed first part of critical bit │ 0.333333s │
│ - completed second part of critical bit │ 0.111000s │
│ /end │ 0.000160s │
’----------------------------------------------------------------+-----------’

which means mysub took 0.555555s overall, it took 0.111111s to reach the critical bit,
the first part of the critical bit took 0.333333s, and the second part 0.111s.

METHODS
new

Constructor.

$stats = Catalyst::Stats->new;

enable

$stats->enable(0);
$stats->enable(1);

Enable or disable stats collection. By default, stats are enabled after object cre-
ation.

profile

$stats->profile($comment);
$stats->profile(begin => $block_name, comment =>$comment);
$stats->profile(end => $block_name);

Marks a profiling point. These can appear in pairs, to time the block of code between
the begin/end pairs, or by themselves, in which case the time of execution to the pre-
vious profiling point will be reported.

The argument may be either a single comment string or a list of name-value pairs.
Thus the following are equivalent:

$stats->profile($comment);
$stats->profile(comment => $comment);

The following key names/values may be used:

* begin => ACTION
Marks the beginning of a block. The value is used in the description in the tim-
ing report.

* end => ACTION
Marks the end of the block. The name given must match a previous ’begin’. Cor-
rect nesting is recommended, although this module is tolerant of blocks that are
not correctly nested, and the reported timings should accurately reflect the time
taken to execute the block whether properly nested or not.

* comment => COMMENT
Comment string; use this to describe the profiling point. It is combined with the
block action (if any) in the timing report description field.

* uid => UID
Assign a predefined unique ID. This is useful if, for whatever reason, you wish
to relate a profiling point to a different parent than in the natural execution
sequence.

* parent => UID
Explicitly relate the profiling point back to the parent with the specified UID.
rect nesting is recommended, although this module is tolerant of blocks that are
not correctly nested, and the reported timings should accurately reflect the time
taken to execute the block whether properly nested or not.

* comment => COMMENT
Comment string; use this to describe the profiling point. It is combined with the
block action (if any) in the timing report description field.

* uid => UID
Assign a predefined unique ID. This is useful if, for whatever reason, you wish
to relate a profiling point to a different parent than in the natural execution
sequence.

* parent => UID
Explicitly relate the profiling point back to the parent with the specified UID.
The profiling point will be ignored if the UID has not been previously defined.

Returns the UID of the current point in the profile tree. The UID is automatically
assigned if not explicitly given.

elapsed

$elapsed = $stats->elapsed

Get the total elapsed time (in seconds) since the object was created.

report

print $stats->report ."\n";
$report = $stats->report;
@report = $stats->report;

In scalar context, generates a textual report. In array context, returns the array of
results where each row comprises:

[ depth, description, time, rollup ]

The depth is the calling stack level of the profiling point.

The description is a combination of the block name and comment.

The time reported for each block is the total execution time for the block, and the
time associated with each intermediate profiling point is the elapsed time from the
previous profiling point.

The ’rollup’ flag indicates whether the reported time is the rolled up time for the
block, or the elapsed time from the previous profiling point.

SEE ALSO
Catalyst.

AUTHOR
Jon Schutz

COPYRIGHT
This program is free software, you can redistribute it and/or modify it under the same
terms as Perl itself.

perl v5.8.8 Catalyst-Runtime-5.7010::lib::Catalyst::Stats(3)
Attachments: catalyst-stats.patch (19.4 KB)


jon at jrock

Oct 18, 2007, 9:26 PM

Post #6 of 10 (2318 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

Jon Schutz wrote:
> Patch against Catalyst-Runtime-5.7010 is attached, inc doc and tests.

Thanks! Applied as r7027 and r7028. In r7029, I changed your tabs to
spaces to make the perl critic test pass. (r7028 is there because I
resolved conflicts in the wrong direction; oops.)

In the future, it would be best to send patches against svn trunk. That
way, you'll get the critic test (which is disabled for CPAN releases),
and I won't have
to resolve conflicts (incorrectly) when I apply the patch. :)

Since I might have changed some stuff incorrectly, please let me know if
trunk works as expected.

BTW, I finally got a git mirror of the catalyst repo going. To apply
your patch (patch -p1 didn't like it), I made a branch from the 5.7010
release, applied your patch, and then had git merge into trunk. Then, I
resolved the conflicts and pushed to the svn repository. It was a
smashing good time :)

Here's a diagram:

http://www.jrock.us/catalyst-git.png

git++ :)

Regards,
Jonathan Rockway
Attachments: signature.asc (0.36 KB)


jon at jrock

Oct 18, 2007, 9:29 PM

Post #7 of 10 (2321 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

Jonathan Rockway wrote:
> Jon Schutz wrote:
>
>> Patch against Catalyst-Runtime-5.7010 is attached, inc doc and tests.
>>
>
> Thanks! Applied as r7027 and r7028.

One other thing. Before your patch, the debugging output looks like:

.----------------------------------------------------------------+-----------.
| Action |
Time |
+----------------------------------------------------------------+-----------+
| /dump/request |
0.002445s |
| -> TestApp::View::Dump::Request->process |
0.000449s |
| /end |
0.000030s |
'----------------------------------------------------------------+-----------'

After, it looks like:
.----------------------------------------------------------------+-----------.
| Action |
Time |
+----------------------------------------------------------------+-----------+
| /dump/request |
0.002161s |
| |
0.001973s |
| /end |
0.000132s |
'----------------------------------------------------------------+-----------'

Is that expected?

Regards,
Jonathan Rockway
Attachments: signature.asc (0.36 KB)


jon+catalyst at youramigo

Oct 18, 2007, 9:51 PM

Post #8 of 10 (2317 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

On Thu, 2007-10-18 at 23:29 -0500, Jonathan Rockway wrote:
> Jonathan Rockway wrote:
> > Jon Schutz wrote:
> >
> >> Patch against Catalyst-Runtime-5.7010 is attached, inc doc and tests.
> >>
> >
> > Thanks! Applied as r7027 and r7028.
>
> One other thing. Before your patch, the debugging output looks like:
>

Hmmm, no the output was identical when I checked.

I'll get a hold of svn trunk and have a look.

Thanks,

--

Jon


_______________________________________________
Catalyst-dev mailing list
Catalyst-dev [at] lists
http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst-dev


jon+catalyst at youramigo

Oct 18, 2007, 11:42 PM

Post #9 of 10 (2310 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

On Thu, 2007-10-18 at 23:29 -0500, Jonathan Rockway wrote:
> Jonathan Rockway wrote:
> > Jon Schutz wrote:
> >
> >> Patch against Catalyst-Runtime-5.7010 is attached, inc doc and tests.
> >>
> >
> > Thanks! Applied as r7027 and r7028.
>
> One other thing. Before your patch, the debugging output looks like:

OK, sorry 'bout that; there's a bit of mucking around with UIDs which I
suspect is unnecessary but I've preserved it, at least tried to, and got
it wrong first time round.

svn diff attached.

Regards,

--

Jon
Attachments: cat-stats.diff (1.57 KB)


jon at jrock

Oct 21, 2007, 9:12 PM

Post #10 of 10 (2293 views)
Permalink
Re: Proposal for built-in profiling function [In reply to]

Jon Schutz wrote:
> OK, sorry 'bout that; there's a bit of mucking around with UIDs which I
> suspect is unnecessary but I've preserved it, at least tried to, and got
> it wrong first time round.
>
>

Hi Jon, applied as r7039. Thanks!

Regards,
Jonathan Rockway
Attachments: signature.asc (0.36 KB)

Catalyst dev RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.