File Coverage

blib/lib/Devel/NYTProf/Callgrind/TicksDiff.pm
Criterion Covered Total %
statement 84 90 93.3
branch 16 22 72.7
condition 3 3 100.0
subroutine 11 12 91.6
pod 5 5 100.0
total 119 132 90.1


line stmt bran cond sub pod time code
1             package Devel::NYTProf::Callgrind::TicksDiff; # Calculates a delta between 2 callgrind files
2              
3 3     3   14129 use v5.10;
  3         7  
4 3     3   9 use strict;
  3         3  
  3         48  
5 3     3   7 use warnings;
  3         9  
  3         62  
6 3     3   1102 use Devel::NYTProf::Callgrind::Ticks;
  3         7  
  3         96  
7 3     3   18 use Carp;
  3         3  
  3         231  
8              
9             our $VERSION = '0.04';
10              
11             # If you do a performance analysis with NYTProf over different
12             # computers and want to know what makes the application
13             # slower on the second machine, it might be usefull to
14             # see the difference.
15             #
16             # TicksDiff takes the callgrind files, you can get with
17             # nytprofcg and calculates the delta between 2 files to
18             # a new callgrind file.
19             #
20             # It is nice to open the resulting file with kcachegrind to
21             # see it in a graphical way.
22             #
23             # SYNOPSIS
24             # ========
25             #
26             # The command line way:
27             #
28             # # Output to STDOUT
29             # callgrind diff fileA.callgrind fileB.callgrind
30             #
31             # # Output to a file
32             # callgrind diff fileA.callgrind fileB.callgrind --out callgrind
33             #
34             # # With normalization (see below)
35             # callgrind diff fileA.callgrind fileB.callgrind --out callgrind --normalize
36             #
37             #
38             # The Perl way:
39             #
40             # use Devel::NYTProf::Callgrind::TicksDiff;
41             # my $tickdiff = Devel::NYTProf::Callgrind::TicksDiff->new( files => [$fileA,$fileB], normalize => 1 );
42             # print $ticksdiff->getDiffText();
43             #
44             #
45             # Normalize
46             # =========
47             # The comand line and the contructor can take the argument 'normalize'.
48             # It will avoid to truncate negative values.
49             #
50             # To understand it, ive got to explain what happens in TicksDiff:
51             # If you have to runs of a perl script (Run A and B) with different amount of ticks.
52             #
53             # A B function
54             # 100 120 foo()
55             # 120 100 bar()
56             #
57             # And you make a diff of it, TicksDiff would assume, you want to know how many ticks MORE the run B needs than A.
58             # The result would be:
59             #
60             # A B diff function
61             # 100 120 20 foo()
62             # 120 100 0 bar() # -20 is the real diff
63             #
64             # The negative values will ne truncated to 0 because it is not possible to have negative ticks
65             # (maybe in a black whole ;-)
66             #
67             # If you dont want that truncation, you can raise the whole level with the biggest negative value.
68             # So the result would be:
69             #
70             # A B normalized diff function
71             # 100 120 40 foo()
72             # 120 100 0 bar()
73             #
74             #
75             # AUTHOR
76             # ======
77             # Andreas Hernitscheck - ahernit AT cpan.org
78             #
79             # LICENCE
80             # =======
81             # You can redistribute it and/or modify it under the conditions of
82             # LGPL and Artistic Licence.
83              
84              
85 3     3   11 use Moose;
  3         4  
  3         9  
86              
87             # callgrind files to be compared
88             has 'files' => (
89             is => 'rw',
90             isa => 'ArrayRef',
91             required => 1,
92             default => sub {[]},
93             );
94              
95              
96              
97             has 'file_out' => (
98             is => 'rw',
99             isa => 'ArrayRef',
100             default => sub {[]},
101             );
102              
103             # Objects of ticks Devel::NYTProf::Callgrind::TicksD
104             has 'ticks_objects' => (
105             is => 'rw',
106             isa => 'ArrayRef',
107             builder => '_loadFiles',
108             );
109              
110              
111             has 'ticks_object_out' => (
112             is => 'rw',
113             default => undef,
114             );
115              
116             # enable normalization
117             has 'normalize' => (
118             is => 'rw',
119             isa => 'Bool',
120             default => 0,
121             );
122              
123              
124             # if negative ticks are allowed or be truncated to 0
125             has 'allow_negative' => (
126             is => 'rw',
127             isa => 'Bool',
128             default => 0,
129             );
130              
131              
132             sub _loadFiles{
133 2     2   1326 my $self = shift;
134 2 100       60 my $reffiles = $self->files() or croak("files must be set");
135 1         1 my @files = @{ $reffiles };
  1         2  
136 1         1 my @objs;
137              
138 1         1 foreach my $file (@files){
139            
140 2         15 my $ticks = Devel::NYTProf::Callgrind::Ticks->new( file => $file );
141 2         15 push @objs, $ticks;
142             }
143              
144            
145 1         30 $self->ticks_objects( \@objs );
146            
147 1         3 return \@objs; # for Moose builder
148             }
149              
150              
151              
152             # starts the compare process. So far it compares only
153             # two files. Returning infos in a hash.
154             sub compare{ # HashRef
155 3     3 1 8 my $self = shift;
156 3         120 my $objs = $self->ticks_objects();
157 3         4 my $result = {};
158              
159 3         3 my $obj_a = $objs->[0];
160 3         3 my $obj_b = $objs->[1];
161              
162 3         3 my $notfound = 0;
163 3         13 my $delta_total = 0;
164 3         3 my $delta_less = 0;
165 3         1 my $delta_more = 0;
166 3         3 my $max_less = 0;
167              
168              
169             ## remember deltas for new blocks
170 3         3 my $deltaInfo = [];
171              
172 3         3 foreach my $block_a ( @{ $obj_a->list() } ){
  3         78  
173              
174 21         34 my $block_b = $obj_b->getBlockEquivalent( $block_a );
175              
176 21 50       25 if ( $block_b ){
177 21         24 my $delta = $self->diffBlocks( $block_a, $block_b );
178 21         20 $delta_total += $delta;
179            
180 21 100       22 if ( $delta > 0 ){
181 12         5 $delta_more += $delta;
182             }else{
183 9         6 $delta_less += $delta;
184              
185             # remember the biggest negative value.
186             # to enable shifting when normalize is on
187 9 100       14 if ( $delta < $max_less ){
188 6         2 $max_less = $delta;
189             }
190             }
191              
192 21         50 push @$deltaInfo, {
193             delta => $delta,
194             block_a => $block_a,
195             block_b => $block_b,
196             };
197              
198              
199             #print $delta."\n";
200              
201             }else{
202 0         0 $notfound++;
203             }
204              
205             }
206              
207              
208              
209             ## build new delta blocks.
210             ## iterate over the stored delta info list with
211             ## refs to the original blocks
212            
213             ## new ticks object to store the delta info in
214 3         10 my $nobj = Devel::NYTProf::Callgrind::Ticks->new();
215 3         93 my $norm = $self->normalize();
216 3         82 my $allow_negative = $self->allow_negative();
217 3         3 foreach my $deltaInfo ( @{ $deltaInfo } ){
  3         8  
218              
219 21         33 my $block_a = $deltaInfo->{'block_a'};
220              
221             ## now build a new block
222 21         19 my $nblock = {};
223 21         14 %{ $nblock } = %{ $block_a }; # copy the existing block
  21         67  
  21         31  
224              
225 21 50       41 if ( scalar( keys %$nblock ) == 0 ){ next }; # skip empty
  0         0  
226              
227 21         8 my $nticks = $deltaInfo->{'delta'}; # using the delta as ticks
228            
229             # normalization?
230             # It will shift up all values by the maximum nagative delta
231             # to have the lowest value as 0.
232 21 100       30 if ( $norm ){
233 7         3 $nticks = $nticks - $max_less; # it is a negative value
234             }
235              
236             ## do not allow negative deltas.
237             ## to avoid wrong info, you may use normalize
238 21 100 100     42 if ( ($nticks < 0) && (!$allow_negative)){
239 3         3 $nticks = 0;
240             }
241              
242 21         14 $nblock->{'ticks'} = $nticks;
243            
244             # store to the new ticks object
245 21         36 $nobj->addBlock( $nblock );
246             }
247            
248             ## save to official location
249 3         83 $self->ticks_object_out( $nobj );
250              
251              
252 3         6 $result = {
253             not_found => $notfound,
254             delta_more => $delta_more,
255             delta_less => $delta_less,
256             delta_total => $delta_total,
257             max_less => $max_less,
258             };
259              
260              
261              
262              
263 3         13 return $result;
264             }
265              
266              
267             # Compares two single blocks (HasRefs) provided
268             # by the Ticks class of this package. It returns
269             # the tick difference between B and A. Means
270             # B-Ticks - A-Ticks.
271             sub diffBlocks{ # $ticks ( \%blockA, \%blockB )
272 21     21 1 12 my $self = shift;
273 21 50       23 my $blocka = shift or die "block as hashref required";
274 21 50       22 my $blockb = shift or die "block as hashref required";
275            
276 21         18 my $ta = $blocka->{'ticks'};
277 21         17 my $tb = $blockb->{'ticks'};
278              
279 21         26 return $tb - $ta;
280             }
281              
282              
283             # just a wrapper around ticks_object_out
284             sub getDeltaTicksObject{ # $Object
285 3     3 1 1737 my $self = shift;
286              
287 3         90 return $self->ticks_object_out();
288             }
289              
290              
291             # Saves the difference to a callgrind file
292             sub saveDiffFile{ # void ( $filename )
293 1     1 1 314 my $self = shift;
294 1 50       3 my $file = shift or die "need filename";
295              
296 1         39 my $obj = $self->ticks_object_out();
297 1         4 $obj->saveFile( $file );
298              
299 1 50       20 if ( ! -f $file ){ die "Did not create file $file" };
  0            
300              
301             }
302              
303             # Returns the callgrind text of the diff.
304             sub getDiffText{ # $text
305 0     0 1   my $self = shift;
306              
307 0           my $obj = $self->ticks_object_out();
308 0           return $obj->getAsText();
309             }
310              
311              
312             1;
313              
314              
315             #################### pod generated by Pod::Autopod - keep this line to make pod updates possible ####################
316              
317             =head1 NAME
318              
319             Devel::NYTProf::Callgrind::TicksDiff - Calculates a delta between 2 callgrind files
320              
321              
322             =head1 SYNOPSIS
323              
324              
325             The command line way:
326              
327             # Output to STDOUT
328             callgrind diff fileA.callgrind fileB.callgrind
329              
330             # Output to a file
331             callgrind diff fileA.callgrind fileB.callgrind --out callgrind
332              
333             # With normalization (see below)
334             callgrind diff fileA.callgrind fileB.callgrind --out callgrind --normalize
335              
336              
337             The Perl way:
338              
339             use Devel::NYTProf::Callgrind::TicksDiff;
340             my $tickdiff = Devel::NYTProf::Callgrind::TicksDiff->new( files => [$fileA,$fileB], normalize => 1 );
341             print $ticksdiff->getDiffText();
342              
343              
344              
345              
346             =head1 DESCRIPTION
347              
348             If you do a performance analysis with NYTProf over different
349             computers and want to know what makes the application
350             slower on the second machine, it might be usefull to
351             see the difference.
352              
353             TicksDiff takes the callgrind files, you can get with
354             nytprofcg and calculates the delta between 2 files to
355             a new callgrind file.
356              
357             It is nice to open the resulting file with kcachegrind to
358             see it in a graphical way.
359              
360              
361              
362             =head1 REQUIRES
363              
364             L<Devel::NYTProf::Callgrind::TicksDiff>
365              
366             L<Moose>
367              
368             L<Devel::NYTProf::Callgrind::Ticks>
369              
370              
371             =head1 METHODS
372              
373              
374             =head2 compare
375              
376             my \%hashref = $this->compare();
377              
378             starts the compare process. So far it compares only
379             two files. Returning infos in a hash.
380              
381              
382             =head2 diffBlocks
383              
384             my $ticks = $this->diffBlocks(\%blockA, \%blockB);
385              
386             Compares two single blocks (HasRefs) provided
387             by the Ticks class of this package. It returns
388             the tick difference between B and A. Means
389             B-Ticks - A-Ticks.
390              
391              
392             =head2 getDeltaTicksObject
393              
394             my $Object = $this->getDeltaTicksObject();
395              
396             just a wrapper around ticks_object_out
397              
398              
399             =head2 getDiffText
400              
401             my $text = $this->getDiffText();
402              
403             Returns the callgrind text of the diff.
404              
405              
406             =head2 saveDiffFile
407              
408             $this->saveDiffFile($filename);
409              
410             Saves the difference to a callgrind file
411              
412              
413              
414             =head1 LICENCE
415              
416             You can redistribute it and/or modify it under the conditions of
417             LGPL and Artistic Licence.
418              
419              
420             =head1 Normalize
421              
422             The comand line and the contructor can take the argument 'normalize'.
423             It will avoid to truncate negative values.
424              
425             To understand it, ive got to explain what happens in TicksDiff:
426             If you have to runs of a perl script (Run A and B) with different amount of ticks.
427              
428             A B function
429             100 120 foo()
430             120 100 bar()
431              
432             And you make a diff of it, TicksDiff would assume, you want to know how many ticks MORE the run B needs than A.
433             The result would be:
434              
435             A B diff function
436             100 120 20 foo()
437             120 100 0 bar() # -20 is the real diff
438              
439             The negative values will ne truncated to 0 because it is not possible to have negative ticks
440             (maybe in a black whole ;-)
441              
442             If you dont want that truncation, you can raise the whole level with the biggest negative value.
443             So the result would be:
444              
445             A B normalized diff function
446             100 120 40 foo()
447             120 100 0 bar()
448              
449              
450              
451              
452             =head1 AUTHOR
453              
454             Andreas Hernitscheck - ahernit AT cpan.org
455              
456              
457              
458             =cut
459