File Coverage

blib/lib/Devel/NYTProf/Callgrind/TicksDiff.pm
Criterion Covered Total %
statement 84 90 93.3
branch 15 22 68.1
condition 3 3 100.0
subroutine 11 12 91.6
pod 5 5 100.0
total 118 132 89.3


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   15208 use v5.10;
  3         7  
4 3     3   9 use strict;
  3         4  
  3         50  
5 3     3   8 use warnings;
  3         11  
  3         96  
6 3     3   1088 use Devel::NYTProf::Callgrind::Ticks;
  3         6  
  3         100  
7 3     3   20 use Carp;
  3         5  
  3         287  
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   15 use Moose;
  3         6  
  3         14  
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   1522 my $self = shift;
134 2 50       73 my $reffiles = $self->files() or croak("files must be set");
135 2         2 my @files = @{ $reffiles };
  2         5  
136 2         4 my @objs;
137              
138 2         3 foreach my $file (@files){
139            
140 4         40 my $ticks = Devel::NYTProf::Callgrind::Ticks->new( file => $file );
141 4         33 push @objs, $ticks;
142             }
143              
144            
145 2         125 $self->ticks_objects( \@objs );
146            
147 2         8 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 4     4 1 12 my $self = shift;
156 4         133 my $objs = $self->ticks_objects();
157 4         7 my $result = {};
158              
159 4         5 my $obj_a = $objs->[0];
160 4         7 my $obj_b = $objs->[1];
161              
162 4         4 my $notfound = 0;
163 4         6 my $delta_total = 0;
164 4         3 my $delta_less = 0;
165 4         4 my $delta_more = 0;
166 4         5 my $max_less = 0;
167              
168              
169             ## remember deltas for new blocks
170 4         5 my $deltaInfo = [];
171              
172 4         5 foreach my $block_a ( @{ $obj_a->list() } ){
  4         117  
173              
174 28         54 my $block_b = $obj_b->getBlockEquivalent( $block_a );
175              
176 28 50       37 if ( $block_b ){
177 28         53 my $delta = $self->diffBlocks( $block_a, $block_b );
178 28         20 $delta_total += $delta;
179            
180 28 100       32 if ( $delta > 0 ){
181 19         18 $delta_more += $delta;
182             }else{
183 9         8 $delta_less += $delta;
184              
185             # remember the biggest negative value.
186             # to enable shifting when normalize is on
187 9 100       12 if ( $delta < $max_less ){
188 6         6 $max_less = $delta;
189             }
190             }
191              
192 28         81 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 4         22 my $nobj = Devel::NYTProf::Callgrind::Ticks->new();
215 4         136 my $norm = $self->normalize();
216 4         118 my $allow_negative = $self->allow_negative();
217 4         7 foreach my $deltaInfo ( @{ $deltaInfo } ){
  4         8  
218              
219 28         87 my $block_a = $deltaInfo->{'block_a'};
220              
221             ## now build a new block
222 28         25 my $nblock = {};
223 28         20 %{ $nblock } = %{ $block_a }; # copy the existing block
  28         106  
  28         364  
224              
225 28 50       60 if ( scalar( keys %$nblock ) == 0 ){ next }; # skip empty
  0         0  
226              
227 28         24 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 28 100       41 if ( $norm ){
233 7         8 $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 28 100 100     57 if ( ($nticks < 0) && (!$allow_negative)){
239 3         3 $nticks = 0;
240             }
241              
242 28         25 $nblock->{'ticks'} = $nticks;
243            
244             # store to the new ticks object
245 28         58 $nobj->addBlock( $nblock );
246             }
247            
248             ## save to official location
249 4         122 $self->ticks_object_out( $nobj );
250              
251              
252 4         19 $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 4         19 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 28     28 1 25 my $self = shift;
273 28 50       47 my $blocka = shift or die "block as hashref required";
274 28 50       33 my $blockb = shift or die "block as hashref required";
275            
276 28         35 my $ta = $blocka->{'ticks'};
277 28         23 my $tb = $blockb->{'ticks'};
278              
279 28         47 return $tb - $ta;
280             }
281              
282              
283             # just a wrapper around ticks_object_out
284             sub getDeltaTicksObject{ # $Object
285 4     4 1 2836 my $self = shift;
286              
287 4         138 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 322 my $self = shift;
294 1 50       5 my $file = shift or die "need filename";
295              
296 1         42 my $obj = $self->ticks_object_out();
297 1         4 $obj->saveFile( $file );
298              
299 1 50       18 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