Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
file_stats.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * gathers statistics from all file modules.
25  */
26 
27 #include "precompiled.h"
29 
30 #include <set>
31 
32 #include "lib/timer.h"
33 
34 #if FILE_STATS_ENABLED
35 
36 // vfs
37 static size_t vfs_files;
38 static double vfs_size_total;
39 static double vfs_init_elapsed_time;
40 
41 // file
42 static size_t unique_names;
43 static size_t unique_name_len_total;
44 static size_t open_files_cur, open_files_max; // total = opened_files.size()
45 
46 // file_buf
47 static size_t extant_bufs_cur, extant_bufs_max, extant_bufs_total;
48 static double buf_size_total, buf_aligned_size_total;
49 
50 // file_io
51 static size_t user_ios;
52 static double user_io_size_total;
53 static double io_actual_size_total[FI_MAX_IDX][2];
54 static double io_elapsed_time[FI_MAX_IDX][2];
55 static double io_process_time_total;
56 static size_t io_seeks;
57 
58 // file_cache
59 static size_t cache_count[2];
60 static double cache_size_total[2];
61 static size_t conflict_misses;
62 //static double conflict_miss_size_total; // JW: currently not used nor computed
63 static size_t block_cache_count[2];
64 
65 // archive builder
66 static size_t ab_connection_attempts; // total number of trace entries
67 static size_t ab_repeated_connections; // how many of these were not unique
68 
69 
70 // convenience functions for measuring elapsed time in an interval.
71 // by exposing start/finish calls, we avoid callers from querying
72 // timestamps when stats are disabled.
73 static double start_time;
74 static void timer_start(double* start_time_storage = &start_time)
75 {
76  // make sure no measurement is currently active
77  // (since start_time is shared static storage)
78  ENSURE(*start_time_storage == 0.0);
79  *start_time_storage = timer_Time();
80 }
81 static double timer_reset(double* start_time_storage = &start_time)
82 {
83  double elapsed = timer_Time() - *start_time_storage;
84  *start_time_storage = 0.0;
85  return elapsed;
86 }
87 
88 //-----------------------------------------------------------------------------
89 
90 //
91 // vfs
92 //
93 
94 void stats_vfs_file_add(size_t file_size)
95 {
96  vfs_files++;
97  vfs_size_total += file_size;
98 }
99 
100 void stats_vfs_file_remove(size_t file_size)
101 {
102  vfs_files--;
103  vfs_size_total -= file_size;
104 }
105 
106 // stats_vfs_init_* are currently unused
108 {
109  timer_start();
110 }
111 
113 {
114  vfs_init_elapsed_time += timer_reset();
115 }
116 
117 
118 //
119 // file
120 //
121 
122 void stats_unique_name(size_t name_len)
123 {
124  unique_names++;
125  unique_name_len_total += name_len;
126 }
127 
128 
129 void stats_open()
130 {
131  open_files_cur++;
132  open_files_max = std::max(open_files_max, open_files_cur);
133 
134  // could also use a set to determine unique files that have been opened
135 }
136 
137 void stats_close()
138 {
139  ENSURE(open_files_cur > 0);
140  open_files_cur--;
141 }
142 
143 
144 //
145 // file_buf
146 //
147 
148 void stats_buf_alloc(size_t size, size_t alignedSize)
149 {
150  extant_bufs_cur++;
151  extant_bufs_max = std::max(extant_bufs_max, extant_bufs_cur);
152  extant_bufs_total++;
153 
154  buf_size_total += size;
155  buf_aligned_size_total += alignedSize;
156 }
157 
158 void stats_buf_free()
159 {
160  ENSURE(extant_bufs_cur > 0);
161  extant_bufs_cur--;
162 }
163 
164 void stats_buf_ref()
165 {
166  extant_bufs_cur++;
167 }
168 
169 
170 //
171 // file_io
172 //
173 
174 void stats_io_user_request(size_t user_size)
175 {
176  user_ios++;
177  user_io_size_total += user_size;
178 }
179 
181 {
182  m_startTime = 0.0;
183  timer_start(&m_startTime);
184 }
185 
187 {
188  // note: we can only bill IOs that have succeeded :S
189  timer_reset(&m_startTime);
190 }
191 
193 {
194  ENSURE(fi < FI_MAX_IDX);
195  ENSURE(opcode == LIO_READ || opcode == LIO_WRITE);
196 
197  io_actual_size_total[fi][opcode == LIO_WRITE] += size;
198  io_elapsed_time[fi][opcode == LIO_WRITE] += timer_reset(&m_startTime);
199 }
200 
201 
202 void stats_cb_start()
203 {
204  timer_start();
205 }
206 
207 void stats_cb_finish()
208 {
209  io_process_time_total += timer_reset();
210 }
211 
212 
213 //
214 // file_cache
215 //
216 
217 void stats_cache(CacheRet cr, size_t size)
218 {
219  ENSURE(cr == CR_HIT || cr == CR_MISS);
220 
221 #if 0
222  if(cr == CR_MISS)
223  {
224  PairIB ret = ever_cached_files.insert(atom_fn);
225  if(!ret.second) // was already cached once
226  {
227  conflict_miss_size_total += size;
228  conflict_misses++;
229  }
230  }
231 #endif
232 
233  cache_count[cr]++;
234  cache_size_total[cr] += size;
235 }
236 
238 {
239  ENSURE(cr == CR_HIT || cr == CR_MISS);
240  block_cache_count[cr]++;
241 }
242 
243 
244 //
245 // archive builder
246 //
247 
248 void stats_ab_connection(bool already_exists)
249 {
250  ab_connection_attempts++;
251  if(already_exists)
252  ab_repeated_connections++;
253 }
254 
255 
256 //-----------------------------------------------------------------------------
257 
258 template<typename T> int percent(T num, T divisor)
259 {
260  if(!divisor)
261  return 0;
262  return (int)(100*num / divisor);
263 }
264 
265 void file_stats_dump()
266 {
267  if(!debug_filter_allows(L"FILE_STATS|"))
268  return;
269 
270  const double KB = 1e3; const double MB = 1e6; const double ms = 1e-3;
271 
272  debug_printf(L"--------------------------------------------------------------------------------\n");
273  debug_printf(L"File statistics:\n");
274 
275  // note: we split the reports into several debug_printfs for clarity;
276  // this is necessary anyway due to fixed-size buffer.
277 
278  debug_printf(
279  L"\nvfs:\n"
280  L"Total files: %lu (%g MB)\n"
281  L"Init/mount time: %g ms\n",
282  (unsigned long)vfs_files, vfs_size_total/MB,
283  vfs_init_elapsed_time/ms
284  );
285 
286  debug_printf(
287  L"\nfile:\n"
288  L"Total names: %lu (%lu KB)\n"
289  L"Max. concurrent: %lu; leaked: %lu.\n",
290  (unsigned long)unique_names, (unsigned long)(unique_name_len_total/1000),
291  (unsigned long)open_files_max, (unsigned long)open_files_cur
292  );
293 
294  debug_printf(
295  L"\nfile_buf:\n"
296  L"Total buffers used: %lu (%g MB)\n"
297  L"Max concurrent: %lu; leaked: %lu\n"
298  L"Internal fragmentation: %d%%\n",
299  (unsigned long)extant_bufs_total, buf_size_total/MB,
300  (unsigned long)extant_bufs_max, (unsigned long)extant_bufs_cur,
301  percent(buf_aligned_size_total-buf_size_total, buf_size_total)
302  );
303 
304  debug_printf(
305  L"\nfile_io:\n"
306  L"Total user load requests: %lu (%g MB)\n"
307  L"IO thoughput [MB/s; 0=never happened]:\n"
308  L" lowio: R=%.3g, W=%.3g\n"
309  L" aio: R=%.3g, W=%.3g\n"
310  L"Average size = %g KB; seeks: %lu; total callback time: %g ms\n"
311  L"Total data actually read from disk = %g MB\n",
312  (unsigned long)user_ios, user_io_size_total/MB,
313 #define THROUGHPUT(impl, opcode) (io_elapsed_time[impl][opcode == LIO_WRITE] == 0.0)? 0.0 : (io_actual_size_total[impl][opcode == LIO_WRITE] / io_elapsed_time[impl][opcode == LIO_WRITE] / MB)
314  THROUGHPUT(FI_LOWIO, LIO_READ), THROUGHPUT(FI_LOWIO, LIO_WRITE),
315  THROUGHPUT(FI_AIO , LIO_READ), THROUGHPUT(FI_AIO , LIO_WRITE),
316  user_io_size_total/user_ios/KB, (unsigned long)io_seeks, io_process_time_total/ms,
317  (io_actual_size_total[FI_LOWIO][0]+io_actual_size_total[FI_AIO][0])/MB
318  );
319 
320  debug_printf(
321  L"\nfile_cache:\n"
322  L"Hits: %lu (%g MB); misses %lu (%g MB); ratio: %u%%\n"
323  L"Percent of requested bytes satisfied by cache: %u%%; non-compulsory misses: %lu (%u%% of misses)\n"
324  L"Block hits: %lu; misses: %lu; ratio: %u%%\n",
325  (unsigned long)cache_count[CR_HIT], cache_size_total[CR_HIT]/MB, (unsigned long)cache_count[CR_MISS], cache_size_total[CR_MISS]/MB, percent(cache_count[CR_HIT], cache_count[CR_HIT]+cache_count[CR_MISS]),
326  percent(cache_size_total[CR_HIT], cache_size_total[CR_HIT]+cache_size_total[CR_MISS]), (unsigned long)conflict_misses, percent(conflict_misses, cache_count[CR_MISS]),
327  (unsigned long)block_cache_count[CR_HIT], (unsigned long)block_cache_count[CR_MISS], percent(block_cache_count[CR_HIT], block_cache_count[CR_HIT]+block_cache_count[CR_MISS])
328  );
329 
330  debug_printf(
331  L"\nvfs_optimizer:\n"
332  L"Total trace entries: %lu; repeated connections: %lu; unique files: %lu\n",
333  (unsigned long)ab_connection_attempts, (unsigned long)ab_repeated_connections, (unsigned long)(ab_connection_attempts-ab_repeated_connections)
334  );
335 }
336 
337 #endif // FILE_STATS_ENABLED
#define stats_unique_name(name_len)
Definition: file_stats.h:95
Definition: waio.h:93
#define stats_open()
Definition: file_stats.h:96
#define stats_cb_start()
Definition: file_stats.h:109
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
__int64 off_t
Definition: wposix_types.h:91
bool debug_filter_allows(const wchar_t *text)
indicate if the given text would be printed.
Definition: debug.cpp:119
#define file_stats_dump()
Definition: file_stats.h:114
void NotifyOfSuccess(FileIOImplentation fi, int opcode, off_t size)
Definition: file_stats.h:107
#define stats_cb_finish()
Definition: file_stats.h:110
#define T(string_literal)
Definition: secure_crt.cpp:70
double timer_Time()
Definition: timer.cpp:98
#define stats_io_user_request(user_size)
Definition: file_stats.h:101
#define stats_vfs_file_remove(file_size)
Definition: file_stats.h:92
FileIOImplentation
Definition: file_stats.h:35
Definition: waio.h:92
#define stats_close()
Definition: file_stats.h:97
#define stats_buf_ref()
Definition: file_stats.h:100
#define stats_buf_free()
Definition: file_stats.h:99
#define stats_block_cache(cr)
Definition: file_stats.h:112
#define stats_cache(cr, size)
Definition: file_stats.h:111
#define stats_buf_alloc(size, alignedSize)
Definition: file_stats.h:98
#define stats_vfs_file_add(file_size)
Definition: file_stats.h:91
#define stats_vfs_init_finish()
Definition: file_stats.h:94
#define stats_vfs_init_start()
Definition: file_stats.h:93
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
#define stats_ab_connection(already_exists)
Definition: file_stats.h:113
CacheRet
Definition: file_stats.h:36