754 строки
20 KiB
Plaintext
754 строки
20 KiB
Plaintext
% Copyright (C) 2012-2017,2018 John E. Davis
|
|
%
|
|
% This file is part of the S-Lang Library and may be distributed under the
|
|
% terms of the GNU General Public License. See the file COPYING for
|
|
% more information.
|
|
%---------------------------------------------------------------------------
|
|
% See the slprof script for an example of using the profiler.
|
|
%
|
|
_boseos_info=0;
|
|
_bofeof_info=0;
|
|
|
|
%public variable __DEBUG_PROFILE_;
|
|
|
|
% For lines
|
|
private variable Profile_Info_Type = struct
|
|
{
|
|
name, % (bos/eof)
|
|
num_called, % (bos/bof) num times line called
|
|
num_s_triggered, % (eof/eof) num child statements
|
|
num_f_triggered, % (eof/eof) num functions triggered
|
|
num_self_f, % (eos/) num functions directly called
|
|
num_self_s, % num statements directly called
|
|
self_time, % (eos/eof)
|
|
cum_time, % (eos/eof)
|
|
};
|
|
Profile_Info_Type.num_called = 0;
|
|
Profile_Info_Type.num_s_triggered = 0;
|
|
Profile_Info_Type.num_f_triggered = 0;
|
|
Profile_Info_Type.num_self_f = 0;
|
|
Profile_Info_Type.num_self_s = 0;
|
|
Profile_Info_Type.self_time = 0.0;
|
|
Profile_Info_Type.cum_time = 0.0;
|
|
|
|
private variable L_Info_Table;
|
|
private variable F_Info_Table;
|
|
|
|
private define convert_profile_info_to_array (p)
|
|
{
|
|
variable keys = assoc_get_keys (p);
|
|
variable i, n = length (p);
|
|
|
|
% Filter out the bad entries
|
|
_for i (0, n-1, 1)
|
|
{
|
|
if (keys[i] == "") % Dummy
|
|
assoc_delete_key (p, keys[i]);
|
|
}
|
|
n = length (p);
|
|
keys = assoc_get_keys (p);
|
|
|
|
variable s = @Profile_Info_Type;
|
|
s.num_called = Int_Type[n];
|
|
s.num_s_triggered = Int_Type[n];
|
|
s.num_f_triggered = Int_Type[n];
|
|
s.num_self_f = Int_Type[n];
|
|
s.num_self_s = Int_Type[n];
|
|
s.self_time = Double_Type[n];
|
|
s.cum_time = Double_Type[n];
|
|
s.name = keys;
|
|
|
|
variable vals = assoc_get_values (p);
|
|
_for i (0, n-1, 1)
|
|
{
|
|
variable v = vals[i];
|
|
s.num_called[i] = v.num_called;
|
|
s.num_s_triggered[i] = v.num_s_triggered;
|
|
s.num_f_triggered[i] = v.num_f_triggered;
|
|
s.num_self_f[i] = v.num_self_f;
|
|
s.num_self_s[i] = v.num_self_s;
|
|
s.self_time[i] = v.self_time;
|
|
s.cum_time[i] = v.cum_time;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
private define new_l_info (name)
|
|
{
|
|
variable l = @Profile_Info_Type;
|
|
L_Info_Table[name] = l;
|
|
l.name = name;
|
|
return l;
|
|
}
|
|
|
|
private define new_f_info (name)
|
|
{
|
|
variable finfo = @Profile_Info_Type;
|
|
F_Info_Table[name] = finfo;
|
|
finfo.name = name;
|
|
return finfo;
|
|
}
|
|
|
|
private variable Function_Stack_Type = struct
|
|
{
|
|
f_info,
|
|
s_counter,
|
|
f_counter,
|
|
self_s_counter,
|
|
self_time,
|
|
cum_time,
|
|
};
|
|
private variable Function_Stack;
|
|
|
|
private variable Line_Stack_Type = struct
|
|
{
|
|
l_info,
|
|
s_counter,
|
|
f_counter,
|
|
self_f_counter,
|
|
self_time,
|
|
cum_time,
|
|
};
|
|
private variable Line_Stack;
|
|
|
|
private variable L_Info;
|
|
private variable L_S_Counter; % bos/eof
|
|
private variable L_F_Counter; % bos/eof
|
|
private variable L_Self_Time; % bof/eof
|
|
private variable L_Cum_Time; % bof/eof
|
|
|
|
private variable F_Info;
|
|
private variable F_S_Counter; % bof
|
|
private variable F_F_Counter; % bof
|
|
private variable F_Self_S; % bos
|
|
private variable F_Self_Time; % bos/eos
|
|
private variable F_Cum_Time; % bos/eos
|
|
|
|
private variable Num_Statement_Counts;
|
|
private variable Num_Fun_Calls;
|
|
private variable Tstart;
|
|
|
|
private variable Null_Struct = struct {dummy};
|
|
|
|
private variable BOS_Depth;
|
|
private variable BOS_Stack_Depth;
|
|
private define push_line_info ()
|
|
{
|
|
variable s = @Line_Stack_Type;
|
|
s.l_info = L_Info;
|
|
s.s_counter = L_S_Counter;
|
|
s.f_counter = L_F_Counter;
|
|
s.self_time = L_Self_Time;
|
|
s.cum_time = L_Cum_Time;
|
|
list_append (Line_Stack, s);
|
|
BOS_Stack_Depth++;
|
|
}
|
|
|
|
private define pop_line_info ()
|
|
{
|
|
variable s = list_pop (Line_Stack, -1);
|
|
L_Info = s.l_info;
|
|
L_Self_Time = s.self_time;
|
|
L_Cum_Time = s.cum_time;
|
|
|
|
L_Info.num_self_f++;
|
|
L_Info.num_s_triggered += (Num_Statement_Counts - s.s_counter);
|
|
L_Info.num_f_triggered += (Num_Fun_Calls - s.f_counter);
|
|
L_F_Counter = Num_Fun_Calls;
|
|
L_S_Counter = Num_Statement_Counts;
|
|
BOS_Stack_Depth--;
|
|
}
|
|
|
|
% This variable tracks sequential calls to the BOS and EOS handlers
|
|
% This can happen when profiled code calls non-profiled, which calls
|
|
% profiled.
|
|
private variable Last_Was_BOS_EOS = 0;
|
|
private variable Dummy_L_Info;
|
|
|
|
private define bos_handler (file, line)
|
|
{
|
|
variable dt = toc() - Tstart;
|
|
|
|
F_Cum_Time += dt;
|
|
F_Self_Time += dt;
|
|
F_Self_S++;
|
|
if (BOS_Depth >= BOS_Stack_Depth)
|
|
{
|
|
#ifexists __DEBUG_PROFILE_
|
|
%vmessage ("# bos pushing (%d,%d)", BOS_Depth,BOS_Stack_Depth);
|
|
#endif
|
|
L_Cum_Time += dt;
|
|
L_Self_Time += dt;
|
|
push_line_info ();
|
|
}
|
|
BOS_Depth = BOS_Stack_Depth;
|
|
|
|
Num_Statement_Counts++;
|
|
|
|
file = sprintf ("%s:%d", file, line);
|
|
variable l = L_Info_Table[file];
|
|
if (l == Null_Struct)
|
|
l = new_l_info (file);
|
|
|
|
l.num_called++;
|
|
|
|
L_Info = l;
|
|
L_S_Counter = Num_Statement_Counts;
|
|
L_F_Counter = Num_Fun_Calls;
|
|
L_Self_Time = 0.0;
|
|
L_Cum_Time = 0.0;
|
|
|
|
#ifexists __DEBUG_PROFILE_
|
|
vmessage ("BOS: (%d, %d) %S", BOS_Depth, BOS_Stack_Depth, file);
|
|
#endif
|
|
Tstart = toc ();
|
|
}
|
|
|
|
private define eos_handler ()
|
|
{
|
|
variable dt = toc()-Tstart;
|
|
|
|
if (BOS_Depth < BOS_Stack_Depth)
|
|
{
|
|
#ifexists __DEBUG_PROFILE_
|
|
%vmessage ("# eos popping (%d,%d)", BOS_Depth,BOS_Stack_Depth);
|
|
#endif
|
|
pop_line_info ();
|
|
}
|
|
BOS_Depth--;
|
|
|
|
L_Self_Time += dt;
|
|
L_Cum_Time += dt;
|
|
F_Cum_Time += L_Cum_Time;
|
|
F_Self_Time += L_Self_Time;
|
|
|
|
L_Info.self_time += L_Self_Time;
|
|
L_Info.cum_time += L_Cum_Time;
|
|
|
|
#ifexists __DEBUG_PROFILE_
|
|
vmessage ("EOS: (%d,%d) %S", BOS_Depth, BOS_Stack_Depth, L_Info.name);
|
|
#endif
|
|
|
|
% This is necessary if a BOF/EOF sequence follows to avoid another
|
|
% push/pop of this object since
|
|
if (Last_Was_BOS_EOS == -1)
|
|
L_Info = Dummy_L_Info;
|
|
|
|
Last_Was_BOS_EOS = -1;
|
|
Tstart = toc();
|
|
}
|
|
|
|
private define bof_handler (fun, file)
|
|
{
|
|
variable dt = toc () - Tstart;
|
|
Num_Fun_Calls++;
|
|
|
|
#ifexists __DEBUG_PROFILE_
|
|
%vmessage ("# bof pushing (%d,%d)", BOS_Depth,BOS_Stack_Depth);
|
|
#endif
|
|
L_Self_Time += dt;
|
|
L_Cum_Time += dt;
|
|
push_line_info ();
|
|
%BOS_Stack_Depth++;
|
|
|
|
variable f = @Function_Stack_Type;
|
|
f.f_info = F_Info;
|
|
f.s_counter = F_S_Counter;
|
|
f.f_counter = F_F_Counter;
|
|
f.self_time = F_Self_Time;
|
|
f.self_s_counter = F_Self_S;
|
|
f.cum_time = F_Cum_Time;
|
|
list_append (Function_Stack, f);
|
|
|
|
file = sprintf ("%S:%S", fun, file);
|
|
variable f_info = F_Info_Table[file];
|
|
if (f_info == Null_Struct)
|
|
f_info = new_f_info (file);
|
|
f_info.num_called++;
|
|
|
|
F_Info = f_info;
|
|
F_S_Counter = Num_Statement_Counts;
|
|
F_F_Counter = Num_Fun_Calls;
|
|
F_Cum_Time = 0.0;
|
|
F_Self_Time = 0.0;
|
|
F_Self_S = 0;
|
|
|
|
#ifexists __DEBUG_PROFILE_
|
|
vmessage ("BOF: (%d,%d) %S", BOS_Depth, BOS_Stack_Depth, file);
|
|
#endif
|
|
Tstart = toc ();
|
|
}
|
|
|
|
private define eof_handler ()
|
|
{
|
|
variable dt = toc() - Tstart;
|
|
|
|
#ifexists __DEBUG_PROFILE_
|
|
%vmessage ("# eof popping (%d,%d)", BOS_Depth,BOS_Stack_Depth);
|
|
#endif
|
|
F_Cum_Time += dt;
|
|
F_Self_Time += dt;
|
|
F_Info.cum_time += F_Cum_Time;
|
|
F_Info.self_time += F_Self_Time;
|
|
F_Info.num_s_triggered += (Num_Statement_Counts - F_S_Counter);
|
|
F_Info.num_f_triggered += (Num_Fun_Calls - F_F_Counter);
|
|
F_Info.num_self_s += F_Self_S;
|
|
|
|
pop_line_info ();
|
|
% Add on time spent in the function to this line's value
|
|
L_Cum_Time += F_Cum_Time;
|
|
|
|
#ifexists __DEBUG_PROFILE_
|
|
vmessage ("EOF: (%d,%d) %S", BOS_Depth, BOS_Stack_Depth, F_Info.name);
|
|
#endif
|
|
|
|
variable f = list_pop (Function_Stack, -1);
|
|
F_Info = f.f_info;
|
|
F_Info.num_self_f++;
|
|
F_S_Counter = f.s_counter;
|
|
F_F_Counter = f.f_counter;
|
|
F_Self_Time = f.self_time;
|
|
F_Self_S = f.self_s_counter;
|
|
F_Cum_Time = f.cum_time;
|
|
|
|
%F_Cum_Time += dt;
|
|
%F_Self_Time += dt;
|
|
|
|
Tstart = toc();
|
|
}
|
|
|
|
% In function-only mode, lines are not profiled. The self-time of the
|
|
% function is its cumulative time minus the cumulative times of the
|
|
% functions that it directly calls. The F_Self_Time variable will
|
|
% track those.
|
|
private define f_bof_handler (fun, file)
|
|
{
|
|
variable dt = toc () - Tstart;
|
|
Num_Fun_Calls++;
|
|
|
|
variable f = @Function_Stack_Type;
|
|
f.f_info = F_Info;
|
|
f.f_counter = F_F_Counter;
|
|
f.self_time = F_Self_Time;
|
|
f.cum_time = F_Cum_Time + dt;
|
|
list_append (Function_Stack, f);
|
|
|
|
file = sprintf ("%S:%S", fun, file);
|
|
variable f_info = F_Info_Table[file];
|
|
if (f_info == Null_Struct)
|
|
f_info = new_f_info (file);
|
|
f_info.num_called++;
|
|
|
|
F_Info = f_info;
|
|
F_F_Counter = Num_Fun_Calls;
|
|
F_Cum_Time = 0.0;
|
|
F_Self_Time = 0.0;
|
|
|
|
Tstart = toc ();
|
|
}
|
|
|
|
private define f_eof_handler ()
|
|
{
|
|
variable dt = toc() - Tstart;
|
|
|
|
F_Cum_Time += dt;
|
|
F_Info.cum_time += F_Cum_Time;
|
|
F_Info.self_time += (F_Cum_Time - F_Self_Time);
|
|
F_Info.num_f_triggered += (Num_Fun_Calls - F_F_Counter);
|
|
|
|
variable f = list_pop (Function_Stack, -1);
|
|
F_Info = f.f_info;
|
|
F_Info.num_self_f++;
|
|
F_F_Counter = f.f_counter;
|
|
F_Self_Time = f.self_time + F_Cum_Time;
|
|
F_Cum_Time += f.cum_time;
|
|
|
|
Tstart = toc();
|
|
}
|
|
|
|
% Usage: profile_on (do_line_profile)
|
|
define profile_on ()
|
|
{
|
|
_boseos_info = 0;
|
|
_bofeof_info = 1;
|
|
if (_NARGS)
|
|
{
|
|
variable arg = ();
|
|
if (arg)
|
|
_boseos_info = 3;
|
|
}
|
|
}
|
|
|
|
define profile_off ()
|
|
{
|
|
_boseos_info = 0;
|
|
_bofeof_info = 0;
|
|
}
|
|
|
|
private define setup_profiler_variables ()
|
|
{
|
|
L_Info_Table = Assoc_Type[Struct_Type, Null_Struct];
|
|
F_Info_Table = Assoc_Type[Struct_Type, Null_Struct];
|
|
Line_Stack = {};
|
|
Function_Stack = {};
|
|
|
|
Dummy_L_Info = new_l_info ("");
|
|
L_Info = Dummy_L_Info;
|
|
L_S_Counter = 0;
|
|
L_F_Counter = 0;
|
|
L_Self_Time = 0;
|
|
L_Cum_Time = 0;
|
|
|
|
F_Info = new_f_info ("");
|
|
F_S_Counter = 0;
|
|
F_F_Counter = 0;
|
|
F_Self_Time = 0;
|
|
F_Self_S = 0;
|
|
F_Cum_Time = 0;
|
|
|
|
Tstart = 0;
|
|
Num_Statement_Counts = 0;
|
|
Num_Fun_Calls = 0;
|
|
BOS_Stack_Depth=0;
|
|
BOS_Depth=0;
|
|
}
|
|
|
|
define profile_begin ()
|
|
{
|
|
variable arg = 0;
|
|
if (_NARGS)
|
|
arg = ();
|
|
if (arg)
|
|
{
|
|
()=_set_bos_handler (&bos_handler);
|
|
()=_set_eos_handler (&eos_handler);
|
|
()=_set_bof_handler (&bof_handler);
|
|
()=_set_eof_handler (&eof_handler);
|
|
}
|
|
else
|
|
{
|
|
()=_set_bos_handler (NULL);
|
|
()=_set_eos_handler (NULL);
|
|
()=_set_bof_handler (&f_bof_handler);
|
|
()=_set_eof_handler (&f_eof_handler);
|
|
}
|
|
setup_profiler_variables ();
|
|
profile_on (arg);
|
|
tic ();
|
|
}
|
|
|
|
define profile_end ()
|
|
{
|
|
()=_set_bos_handler (NULL);
|
|
()=_set_eos_handler (NULL);
|
|
()=_set_bof_handler (NULL);
|
|
()=_set_eof_handler (NULL);
|
|
profile_off ();
|
|
}
|
|
|
|
% Calibration Notes:
|
|
%
|
|
% If the execution of a statememt causes M other statements to
|
|
% execute and N function calls, then the amount of profiler overhead
|
|
% is given by
|
|
%
|
|
% N*dF + M*dS
|
|
%
|
|
% where dF is the amount of overhead per function call for running
|
|
% the bof/eof-handlers, and dS is the overhead per statement in
|
|
% running the bos/eos-handlers.
|
|
%
|
|
% The timers used to measure the amount of time spent in a function
|
|
% or statement are stopped when a handler is called, and started
|
|
% again when the handler returns. This introduces systematic error
|
|
% in the measurements. Call dt_S the systematic error introduced by
|
|
% bos/eos handlers and dt_F systematic error from the bof/eof handlers.
|
|
% Then the observed time as given by the interval times is related to
|
|
% the "true" time t via
|
|
%
|
|
% t_obs = t + N*dt_F + M*dt_S + dt_S
|
|
%
|
|
% The self-time of the statement t_self is determined through an
|
|
% interval timer that stops when a function is called, and starts
|
|
% again after the function returns. If the statement makes N_self
|
|
% direct function calls, then t_self is related to its observed
|
|
% self-time t_self_obs by
|
|
%
|
|
% t_self_obs = t_self + N_self*dt_F + dt_S
|
|
%
|
|
% The self-time of a function is the sum of the self-times of the
|
|
% individual statements that were executed directly by the function.
|
|
% Suppose the function executes M_self statements that in turn
|
|
% directly call a total of N_self functions. Then, for functions it
|
|
% follows that:
|
|
%
|
|
% t_self_obs_f = t_self_f + N_self*dt_F + M_self*dt_S + dt_F
|
|
%
|
|
% where the last term accounts for the overhead of the bof/eof
|
|
% handler for the function itself.
|
|
%
|
|
% ----------------------------------------------------------------
|
|
%
|
|
% The values dF, dS, dt_S, and dt_F can be determined as follows:
|
|
%
|
|
% With no handlers in place, t can be accurately measured and can be
|
|
% considered t be a known quantity. Suppose the statement has no
|
|
% function calls and let that statement be executed a very large
|
|
% number of times B with handlers in place. Then the elapsed time
|
|
% t_elapsed as given by the wallclock will be given by
|
|
%
|
|
% t_elapsed = B*(t+dS) ===> dS = (t_elapsed - B*t)/B
|
|
%
|
|
% The total measured or observed time t_obs, as given by the
|
|
% profiler's starting and stopping of the clock upon each execution
|
|
% will be given by
|
|
%
|
|
% t_obs = B*(t+dt_S) ===> dt_S = (t_obs - B*t)/B
|
|
%
|
|
% If the statement is just a function call to a function that does
|
|
% nothing, then
|
|
%
|
|
% t_elapsed = B*(t + dS + dF)
|
|
%
|
|
% Using the prior determination of dS, we obtain
|
|
%
|
|
% dF = t_elapsed/B - t - dS
|
|
%
|
|
% For just a single function call with no arguments, the self-time is
|
|
% defined to be 0. So:
|
|
%
|
|
% t_self_obs = 0 + 1*dt_F + dt_S ==> dt_F = t_self_obs-dt_S
|
|
%
|
|
private variable Overhead_Per_Statement = 0.0; % dS
|
|
private variable Error_Per_Statement = 0.0; % dt_S
|
|
private variable Overhead_Per_Function = 0.0; % dF
|
|
private variable Error_Per_Function = 0.0; % dt_F
|
|
|
|
profile_off ();
|
|
|
|
private define cal_nop_0 ();
|
|
private define cal_f_0 (n)
|
|
{
|
|
loop (n)
|
|
{
|
|
cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0();
|
|
cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0();
|
|
cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0();
|
|
cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0(); cal_nop_0();
|
|
}
|
|
}
|
|
|
|
private define cal_s_0 (n)
|
|
{
|
|
loop (n)
|
|
{
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
}
|
|
}
|
|
|
|
profile_on (1);
|
|
|
|
private define cal_nop_1 ();
|
|
private define cal_f_1 (n)
|
|
{
|
|
loop (n)
|
|
{
|
|
cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1();
|
|
cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1();
|
|
cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1();
|
|
cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1(); cal_nop_1();
|
|
}
|
|
}
|
|
private define cal_s_1 (n)
|
|
{
|
|
loop (n)
|
|
{
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
() = 1; () = 1; () = 1; () = 1; () = 1;
|
|
}
|
|
}
|
|
profile_off ();
|
|
|
|
#ifnexists sum
|
|
private define sum (x)
|
|
{
|
|
variable s = 0.0;
|
|
foreach (x) s += ();
|
|
return s;
|
|
}
|
|
#endif
|
|
|
|
define profile_calibrate ()
|
|
{
|
|
#ifexists __DEBUG_PROFILE_
|
|
return;
|
|
#endif
|
|
if (_NARGS == 0) 1000;
|
|
variable n1 = ();
|
|
variable n0 = 100*n1;
|
|
|
|
tic();
|
|
cal_s_0 (n0);
|
|
variable t0 = toc;
|
|
variable t_expected = ((t0*n1)/n0); % when done n1 times
|
|
|
|
profile_begin (1);
|
|
tic ();
|
|
cal_s_1 (n1);
|
|
variable t_elapsed = toc ();
|
|
profile_end ();
|
|
|
|
variable s = convert_profile_info_to_array (L_Info_Table);
|
|
variable num = sum (s.num_called);
|
|
variable t_obs = sum(s.cum_time);
|
|
Error_Per_Statement = (t_obs - t_expected)/num;
|
|
Overhead_Per_Statement = (t_elapsed-t_expected)/num;
|
|
|
|
% Now calibrate the function call overhead
|
|
tic();
|
|
cal_f_0(n0);
|
|
t0 = toc;
|
|
t_expected = ((t0*n1)/n0); % when done n1 times
|
|
|
|
profile_begin (1);
|
|
tic ();
|
|
cal_f_1 (n1);
|
|
t_elapsed = toc ();
|
|
profile_end ();
|
|
|
|
%variable f = convert_func_info_to_array ();
|
|
s = convert_profile_info_to_array (L_Info_Table);
|
|
num = sum (s.num_called);
|
|
Overhead_Per_Function = (t_elapsed - t_expected)/num - Overhead_Per_Statement;
|
|
t_obs = sum(s.self_time);
|
|
Error_Per_Function = t_obs/num - Error_Per_Statement;
|
|
if (Error_Per_Function < 0)
|
|
Error_Per_Function = 0.0;
|
|
}
|
|
|
|
private define profile_report_lines (fp, s)
|
|
{
|
|
s.cum_time -= (Error_Per_Statement * (s.num_called + s.num_s_triggered)
|
|
+ s.num_f_triggered * Error_Per_Function);
|
|
s.self_time -= (Error_Per_Statement * s.num_called
|
|
+ s.num_self_f * Error_Per_Function);
|
|
|
|
variable rates = s.cum_time/s.num_called;
|
|
variable i = array_sort (s.self_time);
|
|
%variable i = array_sort (s.cum_time);
|
|
array_reverse (i);
|
|
rates = rates[i];
|
|
variable num_called = s.num_called[i];
|
|
variable self_time = s.self_time[i];
|
|
variable cum_time = s.cum_time[i];
|
|
variable num_self_f = s.num_self_f[i];
|
|
variable num_s_triggered = s.num_s_triggered[i];
|
|
variable num_f_triggered = s.num_f_triggered[i];
|
|
variable name = s.name[i];
|
|
|
|
#iffalse
|
|
variable total_counts = sum(num_called);
|
|
variable total_calls = sum(num_self_f);
|
|
() = fprintf (fp, "#Number of profiled statements executed: %g\n", total_counts);
|
|
() = fprintf (fp, "#Number of profiled function calls: %g\n", total_calls);
|
|
() = fprintf (fp, "#Profiler Overhead per statement: %g ms\n", Overhead_Per_Statement*1e3);
|
|
() = fprintf (fp, "#Profiler Overhead per function : %g ms\n", Overhead_Per_Function*1e3);
|
|
() = fprintf (fp, "#Profiler Error per statement : %g ms\n", Error_Per_Statement*1e3);
|
|
() = fprintf (fp, "#Profiler Error per function : %g ms\n", Error_Per_Function*1e3);
|
|
() = fprintf (fp, "#Total Profiler Overhead: %g secs\n",
|
|
total_counts * Overhead_Per_Statement + total_calls*Overhead_Per_Function);
|
|
() = fprintf (fp, "\n\n");
|
|
#endif
|
|
|
|
() = fprintf (fp, "#ncalls ms/call totalselfms totalsecs Fcalls Scalls File:line\n");
|
|
% 1234567 1234567890AB 1234567890AB 1234567890AB 1234567 1234567
|
|
_for i (0, length(rates)-1, 1)
|
|
{
|
|
() = fprintf (fp, "%7d %12.5f %12.5f %12.5f %7d %7d %s\n",
|
|
num_called[i], rates[i]*1e3, self_time[i]*1e3,
|
|
cum_time[i], num_f_triggered[i], num_s_triggered[i], name[i]);
|
|
}
|
|
}
|
|
|
|
private define profile_report_funcs (fp, s)
|
|
{
|
|
#ifnfalse
|
|
s.cum_time -= (Error_Per_Statement * s.num_s_triggered
|
|
+ s.num_f_triggered * Error_Per_Function);
|
|
s.self_time -= (Error_Per_Statement * s.num_self_s
|
|
+ s.num_self_f * Error_Per_Function);
|
|
#endif
|
|
variable rates = s.cum_time/s.num_called;
|
|
variable i = array_sort (s.self_time);
|
|
%variable i = array_sort (s.cum_time);
|
|
array_reverse (i);
|
|
rates = rates[i];
|
|
variable num_called = s.num_called[i];
|
|
variable self_time = s.self_time[i];
|
|
variable cum_time = s.cum_time[i];
|
|
variable num_self_f = s.num_self_f[i];
|
|
variable num_self_s = s.num_self_s[i];
|
|
variable num_s_triggered = s.num_s_triggered[i];
|
|
variable num_f_triggered = s.num_f_triggered[i];
|
|
variable name = s.name[i];
|
|
|
|
() = fprintf (fp, "#function ncalls ms/call totalselfms totalsecs Function File\n");
|
|
% 123456789012345678901234 1234567 1234567890AB 1234567890AB 1234567890AB
|
|
_for i (0, length(rates)-1, 1)
|
|
{
|
|
variable func_file = name[i];
|
|
variable j = is_substr (func_file, ":");
|
|
variable func = substr (func_file, 1, j-1);
|
|
variable file = substr (func_file, j+1, -1);
|
|
() = fprintf (fp, "%-24s %7d %12.4f %12.4f %12.4f %7d %7d %s\n",
|
|
func,
|
|
num_called[i], rates[i]*1e3, self_time[i]*1e3,
|
|
cum_time[i], num_self_f[i], num_self_s[i],
|
|
file
|
|
);
|
|
}
|
|
}
|
|
|
|
private define output_title (fp, title)
|
|
{
|
|
variable s = "----------------------------------------------------------------";
|
|
variable spaces = "";
|
|
loop ((strlen(s) - strlen(title))/2)
|
|
spaces = strcat (spaces, " ");
|
|
() = fprintf (fp, "\n#%s\n#%s%s\n#%s\n\n", s, spaces, title, s);;
|
|
}
|
|
|
|
define profile_report (file)
|
|
{
|
|
if (0 == __is_initialized (&L_Info_Table))
|
|
return;
|
|
|
|
variable fp = file;
|
|
if (typeof (file) == String_Type)
|
|
fp = fopen (fp, "w");
|
|
|
|
variable f = convert_profile_info_to_array (F_Info_Table);
|
|
if (length (f.cum_time))
|
|
{
|
|
output_title (fp, "Function Call Profile Report");
|
|
profile_report_funcs (fp, f);
|
|
}
|
|
|
|
variable s = convert_profile_info_to_array (L_Info_Table);
|
|
if (length (s.cum_time))
|
|
{
|
|
output_title (fp, "Line by Line Profile Report");
|
|
profile_report_lines (fp, s);
|
|
}
|
|
|
|
ifnot (_eqs (fp,file))
|
|
() = fclose (fp);
|
|
}
|
|
|
|
provide ("profile");
|