- Undocumented Matlab - https://undocumentedmatlab.com -

Undocumented Profiler options part 3

Posted By Yair Altman On September 26, 2012 | 4 Comments

Today’s article continues my series on undocumented features of the Matlab Profiler. Past articles explained how to profile memory [1], improve timing resolution and fix a font issue [2]. Today’s article will explain undocumented switches of the profile function that can be used to provide detailed history timeline of the function calls. Next week’s article will conclude the series by explaining undocumented switches of the profile function that can be used to profile built-in functions, to report and remove the profiling overhead, plus a couple of as-yet-unsolved mysteries.

Simple call history

One of the features that the programmatic profiling interface provides, and which is not represented in the Profiler GUI, is a report of the exact order in which different functions were called during the profiling session. This profiling history can be turned on using the –history switch. There is also an optional switch of –historysize, which enables us to modify the history size from a default maximum of 1 million function entry and exit items. Here is a sample usage of this history feature:

>> profile('on','-history'); surf(peaks); profile('off')
>> profData = profile('info');
>> history = profData.FunctionHistory
history =
  Columns 1 through 11
     0     0     0     1     0     1     0     1     1     1     0
    19     1    17    17    17    17    18    18     1    19     5
  ...

The history data is actually a numeric matrix, where the first row contains the values 0 (=function entry) or 1 (=function exit), and the second row is the corresponding index into profData.FunctionTable, indicating the called function. We can easily convert this matrix into human-readable form using the following code snippet:

offset = cumsum(1-2*history(1,:)) - 1;  % calling depth
entryIdx = history(1,:)==1;     % history items of function entries
funcIdx = history(2,entryIdx);  % indexes of relevant functions
funcNames = {profData.FunctionTable(funcIdx).FunctionName};
for idx = 1: length(funcNames);
   disp([repmat(' ',1,offset(idx)) funcNames{idx}]);
end

which generates the following calling list in the MATLAB Command Window:

isempty
 isempty
  transpose
 meshgrid
  peaks
 nargchk
  error
 ishg2parent
...

Unfortunately, the history information does not by default contain specific timing of each function entry/exit, but we can still make good use of it by looking at the sequence in which the functions were called from each other.

Detailed call history, with timing information

In order to retrieve actual history timing information, we can run profile with the undocumented/unsupported –timestamp switch, which stores the CPU clock next to the history information. The reported history matrix now has 4 rows rather than 2, where the extra rows represents the timestamp of each function entry/exit:

>> profile('on','-timestamp'); surf(peaks); profile('off')
>> profData = profile('info');
>> profData.FunctionHistory(:,1:3)
ans =
                0                0                1
                1                2                2
       1347473710       1347473710       1347473710
           453000           453000           468000

In this report, the 3rd row represents the timestamp in seconds, while the 4th row represents the fractional portion of the timestamp, in microseconds. In the example above, the first timestamp item corresponds to 1347473710.453 seconds.
The seconds value appears to be related to the number of seconds since midnight Jan 1, 1970 (so-called Epoch [3]), which is a standard time representation in computing systems. However, the actual value appears to be off by slightly over a day from the expected value (which is retrievable via getTime(java.util.Date)) for some unknown reason. Since we are only interested in relative, rather than absolute times when profiling, this minor difference does not affect us.
The timeline of the profiling session can be visualized as follows:

histData = profData.FunctionHistory;
startTime     = histData(3,1) + histData(4,1)/1e6;
relativeTimes = histData(3,:) + histData(4,:)/1e6 - startTime;
plot(relativeTimes);

Profiling history timeline
Profiling history timeline

This report helps us see that a particular set of function calls, around the 100th call mark, is responsible for about 0.5 seconds, a prime candidate for tuning investigation. If we only relied on the standard profiling report we might have missed this because it might have been meshed into the same “bucket” as other invocations of the same function. As illustration, take the following simulated example:

Invocation  #1 of func():  0.500 secs
Invocation  #2 of func():  0.013 secs
Invocation  #3 of func():  0.011 secs
   ...
Invocation #10 of func():  0.012 secs
_____________________________________
Total invocation time:     0.600 secs

In this simulation, we would not have known that the 0.6 secs invocation time of func() is not really evenly distributed across all 10 invocations, and this would lead us to incorrect conclusions. For example, we could spend time unnecessarily on tuning the steady-state run-time performance of the function, whereas we should really have concentrated on only the first invocation. By looking at the actual timestamps we could see the large run-time used by the first invocation and this information can possibly be used to tune this first invocation and significantly reduce the overall time taken by the function.
Note: instead of using profile -timestamp, we could also have used the undocumented built-in function callstats, which is the underlying profiling engine (the profile function is simply a convenient wrapper function to callstats – take a look within profile.m):

callstats('history',2);  % 0 = -nohistory, 1 = -history, 2 = -timestamp

History collection has an overhead, so if you don’t need it then you should turn it off:

profile -nohistory
profile('-nohistory')    % equivalent alternative
callstats('history',0);  % equivalent alternative

Addendum (June 16th, 2014): I have created a utility (profile_history, available on the Matlab File Exchange [4]) that parses all this profile data and presents it in an interactive GUI. See this article [5] for details.

Function call timeline profiling (click for full-size image) [6]
Function call timeline profiling (click for full-size image)

Categories: Desktop, High risk of breaking in future versions, Stock Matlab function, Undocumented feature, Undocumented function


4 Comments (Open | Close)

4 Comments To "Undocumented Profiler options part 3"

#1 Pingback By Function call timeline profiling | Undocumented Matlab On June 16, 2014 @ 14:36

[…] Back in 2012, I wrote an article about the Matlab profiler’s undocumented functionality of storing detailed information […]

#2 Comment By juli On November 2, 2017 @ 10:53

hello,

I tried this code with an very large project I have and regarding the code:

offset = cumsum(1-2*history(1,:)) - 1;  % calling depth
entryIdx = history(1,:)==1;     % history items of function entries
funcIdx = history(2,entryIdx);  % indexes of relevant functions
funcNames = {profData.FunctionTable(funcIdx).FunctionName};
for idx = 1: length(funcNames);
   disp([repmat(' ',1,offset(idx)) funcNames{idx}]);
end

for some reason the line funcNames = {profData.FunctionTable(funcIdx).FunctionName}; is extremely slow and this workaround is faster:

for idx = 1:length(funcIdx);
    fprintf([repmat(' ',1,offset(idx)) profData.FunctionTable(funcIdx(idx)).FunctionName '\n']);
end

#3 Comment By Yair Altman On November 2, 2017 @ 11:42

@Juli – thanks for sharing. Read [13] about the profile_history utility.

#4 Comment By juli On November 3, 2017 @ 10:09

hi Yair,

thanks I have readed and tried but in my case the code evaluated has 156 levels and is very slow to represent the figure and to see the diferent levels.

regards,
j


Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/undocumented-profiler-options-part-3

URLs in this post:

[1] profile memory: http://undocumentedmatlab.com/blog/undocumented-profiler-options/

[2] improve timing resolution and fix a font issue: http://undocumentedmatlab.com/blog/undocumented-profiler-options-part-2/

[3] Epoch: http://en.wikipedia.org/wiki/Unix_time

[4] Matlab File Exchange: http://www.mathworks.com/matlabcentral/fileexchange/46976-profile-history-display-graphical-profiling-timline-data

[5] this article: http://undocumentedmatlab.com/blog/function-call-timeline-profiling

[6] Image: http://undocumentedmatlab.com/images/profile_history.png

[7] Undocumented Profiler options part 4 : https://undocumentedmatlab.com/articles/undocumented-profiler-options-part-4

[8] Undocumented profiler options : https://undocumentedmatlab.com/articles/undocumented-profiler-options

[9] Undocumented Profiler options part 2 : https://undocumentedmatlab.com/articles/undocumented-profiler-options-part-2

[10] Buggy Profiler option : https://undocumentedmatlab.com/articles/buggy-profiler-option

[11] Function call timeline profiling : https://undocumentedmatlab.com/articles/function-call-timeline-profiling

[12] uiundo – Matlab's undocumented undo/redo manager : https://undocumentedmatlab.com/articles/uiundo-matlab-undocumented-undo-redo-manager

[13] : https://undocumentedmatlab.com/blog/function-call-timeline-profiling

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.