FS2_Open
Open source remastering of the Freespace 2 engine
profiling.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Freespace Open 2013. All rights reserved.
3  *
4  * All source code herein is the property of Freespace Open. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 #include "cmdline/cmdline.h"
11 #include "globalincs/pstypes.h"
12 #include "globalincs/systemvars.h"
13 #include "io/timer.h"
14 
15 #include <fstream>
16 
17 //======================CODE TO PROFILE PERFORMANCE=====================
18 
19 /*
20  * Usage information:
21  * In order to gather profiling data for a single function call, you can use the PROFILE macro as defined in
22  * pstypes.h.
23  * Example:
24  * PROFILE("Render", game_render_frame( cid ));
25  *
26  * If you want to profile a block of function calls, you will need to use the profile_begin()/profile_end() calls.
27  * Example:
28  * profile_begin("Some Code");
29  * ...some code...
30  * profile_end("Some Code");
31  * Note that the parameter passed MUST be globally unique across all instances of profiling invocations.
32  *
33  * Profiling invocations can be nested as deep as necessary; this will show up in the readout as indentations.
34  */
35 
38 
41 
43 std::ofstream profiling_file;
44 
49 {
51 
53  {
54  profiling_file.open("profiling.csv");
55 
56  if (!profiling_file.good())
57  {
58  mprintf(("Failed to open profiling output file 'profiling.csv'!"));
59  }
60  }
61 }
62 
64 {
66  {
67  if (profiling_file.is_open())
68  {
69  profiling_file.flush();
70  profiling_file.close();
71  }
72  }
73 }
74 
80 void profile_begin(const char* name)
81 {
83  {
84  int parent = -1;
85  for (int i = 0; i < (int)samples.size(); i++) {
86  if ( !samples[i].open_profiles ) {
87  continue;
88  }
89 
90  samples[i].num_children++;
91 
92  if (samples[i].num_children == 1) {
93  // this is our direct parent for this new sample
94  parent = i;
95  }
96  }
97 
98  for(int i = 0; i < (int)samples.size(); i++) {
99  if( !strcmp(samples[i].name.c_str(), name) && samples[i].parent == parent ) {
100  // found the profile sample
101  samples[i].open_profiles++;
102  samples[i].profile_instances++;
103  samples[i].start_time = timer_get_high_res_microseconds();
104  Assert(samples[i].open_profiles == 1); // max 1 open at once
105  return;
106  }
107  }
108 
109  // create a new profile sample
110  profile_sample new_sample;
111 
112  new_sample.name = SCP_string(name);
113  new_sample.open_profiles = 1;
114  new_sample.profile_instances = 1;
115  new_sample.accumulator = 0;
117  new_sample.children_sample_time = 0;
118  new_sample.num_children = 0;
119  new_sample.parent = parent;
120 
121  samples.push_back(new_sample);
122  }
123 }
124 
130 void profile_end(const char* name)
131 {
132  if (Cmdline_frame_profile) {
133  int num_parents = 0;
134  int child_of = -1;
135 
136  for ( int i = 0; i < (int)samples.size(); i++ ) {
137  if ( samples[i].open_profiles ) {
138  if ( samples[i].num_children == 1 ) {
139  child_of = i;
140  }
141  }
142  }
143 
144  for ( int i = 0; i < (int)samples.size(); i++ ) {
145  if ( !strcmp(samples[i].name.c_str(), name) && samples[i].parent == child_of ) {
146  int inner = 0;
147  int parent = -1;
149  samples[i].open_profiles--;
150 
151  // count all parents and find the immediate parent
152  while ( inner < (int)samples.size() ) {
153  if ( samples[inner].open_profiles > 0 ) {
154  // found a parent (any open profiles are parents)
155  num_parents++;
156 
157  if (parent < 0) {
158  // replace invalid parent (index)
159  parent = inner;
160  }
161  else if (samples[inner].start_time >= samples[parent].start_time) {
162  // replace with more immediate parent
163  parent = inner;
164  }
165  }
166  inner++;
167  }
168 
169  // remember the current number of parents of the sample
170  samples[i].num_parents = num_parents;
171 
172  if ( parent >= 0 ) {
173  // record this time in children_sample_time (add it in)
174  samples[parent].children_sample_time += end_time - samples[i].start_time;
175  }
176 
177  // save sample time in accumulator
178  samples[i].accumulator += end_time - samples[i].start_time;
179 
180  break;
181  }
182  }
183 
184  for (int i = 0; i < (int)samples.size(); i++) {
185  if (samples[i].open_profiles) {
186  samples[i].num_children--;
187  samples[i].num_children = MAX(samples[i].num_children, 0);
188  }
189  }
190  }
191 }
192 
197 {
198  if (Cmdline_frame_profile) {
200 
202  {
204  }
205 
206  profile_output.clear();
207  profile_output += " Avg : Min : Max : # : Profile Name\n";
208  profile_output += "----------------------------------------\n";
209 
210  for(int i = 0; i < (int)samples.size(); i++) {
211  uint sample_time;
212  float percent_time, avg_time, min_time, max_time;
213  uint avg_micro_seconds, min_micro_seconds, max_micro_seconds;
214 
215  Assert(samples[i].open_profiles == 0);
216 
217  sample_time = samples[i].accumulator - samples[i].children_sample_time;
218 
220  percent_time = 0.0f;
221  } else {
222  percent_time = (i2fl(sample_time) / i2fl(end_profile_time - start_profile_time)) *100.0f;
223  }
224 
225  avg_micro_seconds = min_micro_seconds = max_micro_seconds = sample_time;
226  avg_time = min_time = max_time = percent_time;
227 
228  // add new measurement into the history and get avg, min, and max
229  store_profile_in_history(samples[i].name, percent_time, sample_time);
230  get_profile_from_history(samples[i].name, &avg_time, &min_time, &max_time, &avg_micro_seconds, &min_micro_seconds, &max_micro_seconds);
231 
232  // format the data
233  char avg[64], min[64], max[64], num[64];
234 
235  sprintf(avg, "%3.1f%% (%3.1fms)", avg_time, i2fl(avg_micro_seconds)*0.001f);
236  sprintf(min, "%3.1f%% (%3.1fms)", min_time, i2fl(min_micro_seconds)*0.001f);
237  sprintf(max, "%3.1f%% (%3.1fms)", max_time, i2fl(max_micro_seconds)*0.001f);
238  sprintf(num, "%3d", samples[i].profile_instances);
239 
240  SCP_string indented_name(samples[i].name);
241 
242  for(uint indent = 0; indent < samples[i].num_parents; indent++) {
243  indented_name = ">" + indented_name;
244  }
245 
246  char line[256];
247  sprintf(line, "%5s : %5s : %5s : %3s : ", avg, min, max, num);
248 
249  profile_output += line + indented_name + "\n";
250  }
251 
252  samples.clear();
254  }
255 }
256 
263 void store_profile_in_history(SCP_string &name, float percent, uint time)
264 {
265  float old_ratio;
266  float new_ratio = 0.8f * f2fl(Frametime);
267 
268  if(new_ratio > 1.0f) {
269  new_ratio = 1.0f;
270  }
271 
272  old_ratio = 1.0f - new_ratio;
273 
274  for(int i = 0; i < (int)history.size(); i++) {
275  if( history[i].valid && history[i].name == name ) {
276  // found the sample
277  history[i].avg = (history[i].avg * old_ratio) + (percent * new_ratio);
278  history[i].avg_micro_sec = fl2i((history[i].avg_micro_sec * old_ratio) + (time * new_ratio));
279 
280  if( percent < history[i].min ) {
281  history[i].min = percent;
282  } else {
283  history[i].min = (history[i].min*old_ratio) + (percent*new_ratio);
284  }
285 
286  if( time < history[i].min_micro_sec ) {
287  history[i].min_micro_sec = time;
288  } else {
289  history[i].min_micro_sec = fl2i((history[i].min_micro_sec*old_ratio) + (time*new_ratio));
290  }
291 
292  if( percent > history[i].max) {
293  history[i].max = percent;
294  } else {
295  history[i].max = (history[i].max * old_ratio) + (percent * new_ratio);
296  }
297 
298  if( time > history[i].max_micro_sec) {
299  history[i].max_micro_sec = time;
300  } else {
301  history[i].max_micro_sec = fl2i((history[i].max_micro_sec * old_ratio) + (time * new_ratio));
302  }
303 
304  return;
305  }
306  }
307 
308  // add to history
309  profile_sample_history new_history;
310 
311  new_history.name = name;
312  new_history.valid = true;
313  new_history.avg = new_history.min = new_history.max = percent;
314  new_history.avg_micro_sec = new_history.min_micro_sec = new_history.max_micro_sec = time;
315 
316  history.push_back(new_history);
317 }
318 
326 void get_profile_from_history(SCP_string &name, float* avg, float* min, float* max, uint *avg_micro_sec, uint *min_micro_sec, uint *max_micro_sec)
327 {
328  for ( int i = 0; i < (int)history.size(); i++ ) {
329  if ( history[i].name == name ) {
330  *avg = history[i].avg;
331  *min = history[i].min;
332  *max = history[i].max;
333  *avg_micro_sec = history[i].avg_micro_sec;
334  *min_micro_sec = history[i].min_micro_sec;
335  *max_micro_sec = history[i].max_micro_sec;
336  return;
337  }
338  }
339 
340  *avg = *min = *max = 0.0f;
341 }
fix Frametime
Definition: systemvars.cpp:21
int i
Definition: multi_pxo.cpp:466
uint profile_instances
Definition: systemvars.h:200
SCP_string profile_output
Definition: profiling.cpp:42
SCP_vector< profile_sample > samples
Definition: profiling.cpp:36
Assert(pm!=NULL)
SCP_vector< profile_sample_history > history
Definition: profiling.cpp:37
#define mprintf(args)
Definition: pstypes.h:238
bool Cmdline_profile_write_file
Definition: cmdline.cpp:512
GLclampf f
Definition: Glext.h:7097
#define f2fl(fx)
Definition: floating.h:37
void get_profile_from_history(SCP_string &name, float *avg, float *min, float *max, uint *avg_micro_sec, uint *min_micro_sec, uint *max_micro_sec)
Definition: profiling.cpp:326
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
void profile_begin(const char *name)
Definition: profiling.cpp:80
bool Cmdline_frame_profile
Definition: cmdline.cpp:511
void store_profile_in_history(SCP_string &name, float percent, uint time)
Definition: profiling.cpp:263
void profile_init()
Called once at engine initialization to set the timer.
Definition: profiling.cpp:48
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
uint start_profile_time
Definition: profiling.cpp:39
uint end_profile_time
Definition: profiling.cpp:40
uint timer_get_high_res_microseconds()
Definition: timer.cpp:170
unsigned int uint
Definition: pstypes.h:64
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
void profile_dump_output()
Definition: profiling.cpp:196
GLuint const GLchar * name
Definition: Glext.h:5608
SCP_string name
Definition: systemvars.h:203
void profile_end(const char *name)
Definition: profiling.cpp:130
void profile_deinit()
Definition: profiling.cpp:63
GLuint GLuint num
Definition: Glext.h:9089
#define fl2i(fl)
Definition: floating.h:33
std::ofstream profiling_file
Definition: profiling.cpp:43
#define i2fl(i)
Definition: floating.h:32
#define MAX(a, b)
Definition: pstypes.h:299
uint children_sample_time
Definition: systemvars.h:206