2006-04-12 01:59:08 +02:00
|
|
|
/**
|
|
|
|
* =========================================================================
|
|
|
|
* File : file_stats.cpp
|
|
|
|
* Project : 0 A.D.
|
|
|
|
* Description : gathers statistics from all file modules.
|
|
|
|
*
|
|
|
|
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
|
|
* =========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2005-2006 Jan Wassenberg
|
|
|
|
*
|
|
|
|
* Redistribution and/or modification are also permitted under the
|
|
|
|
* terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation (version 2 or later, at your option).
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*/
|
|
|
|
|
2006-01-25 08:21:45 +01:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
#include "lib/timer.h"
|
|
|
|
#include "file_internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
typedef std::set<const char*> AtomFnSet;
|
|
|
|
typedef std::pair<AtomFnSet::iterator, bool> PairIB;
|
|
|
|
|
|
|
|
// vfs
|
|
|
|
static uint vfs_files;
|
|
|
|
static size_t vfs_size_total;
|
|
|
|
static double vfs_init_elapsed_time;
|
|
|
|
|
|
|
|
// file
|
|
|
|
static uint unique_names;
|
|
|
|
static size_t unique_name_len_total;
|
|
|
|
static uint open_files_cur, open_files_max; // total = opened_files.size()
|
|
|
|
static double opened_file_size_total;
|
|
|
|
static AtomFnSet opened_files;
|
|
|
|
|
|
|
|
// file_buf
|
|
|
|
static uint extant_bufs_cur, extant_bufs_max, extant_bufs_total;
|
|
|
|
static double buf_user_size_total, buf_padded_size_total;
|
|
|
|
|
|
|
|
// file_io
|
|
|
|
static uint user_ios;
|
|
|
|
static double user_io_size_total;
|
|
|
|
static double io_actual_size_total[FI_MAX_IDX][2];
|
|
|
|
static double io_elapsed_time[FI_MAX_IDX][2];
|
2006-01-28 23:19:42 +01:00
|
|
|
static double io_process_time_total;
|
2006-01-25 08:21:45 +01:00
|
|
|
static uint io_seeks;
|
|
|
|
|
|
|
|
// file_cache
|
|
|
|
static uint cache_count[2];
|
2006-01-31 08:48:32 +01:00
|
|
|
static double cache_size_total[2];
|
2006-01-25 08:21:45 +01:00
|
|
|
static AtomFnSet ever_cached_files;
|
|
|
|
static uint conflict_misses;
|
2006-01-31 08:48:32 +01:00
|
|
|
static double conflict_miss_size_total;
|
2006-01-25 08:21:45 +01:00
|
|
|
static uint block_cache_count[2];
|
|
|
|
|
2006-03-24 22:56:00 +01:00
|
|
|
// archive builder
|
|
|
|
static uint ab_connection_attempts; // total number of trace entries
|
|
|
|
static uint ab_repeated_connections; // how many of these were not unique
|
2006-01-25 08:21:45 +01:00
|
|
|
|
2006-01-31 08:48:32 +01:00
|
|
|
|
|
|
|
// convenience functions for measuring elapsed time in an interval.
|
|
|
|
// by exposing start/finish calls, we avoid callers from querying
|
|
|
|
// timestamps when stats are disabled.
|
|
|
|
static double start_time;
|
|
|
|
static void timer_start(double* start_time_storage = &start_time)
|
|
|
|
{
|
|
|
|
// make sure no measurement is currently active
|
|
|
|
// (since start_time is shared static storage)
|
|
|
|
debug_assert(*start_time_storage == 0.0);
|
|
|
|
*start_time_storage = get_time();
|
|
|
|
}
|
|
|
|
static double timer_reset(double* start_time_storage = &start_time)
|
|
|
|
{
|
|
|
|
double elapsed = get_time() - *start_time_storage;
|
|
|
|
*start_time_storage = 0.0;
|
|
|
|
return elapsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2006-01-25 08:21:45 +01:00
|
|
|
//
|
|
|
|
// vfs
|
|
|
|
//
|
|
|
|
|
|
|
|
void stats_vfs_file_add(size_t file_size)
|
|
|
|
{
|
|
|
|
vfs_files++;
|
|
|
|
vfs_size_total += file_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stats_vfs_file_remove(size_t file_size)
|
|
|
|
{
|
|
|
|
vfs_files--;
|
|
|
|
vfs_size_total -= file_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void stats_vfs_init_start()
|
|
|
|
{
|
|
|
|
timer_start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void stats_vfs_init_finish()
|
|
|
|
{
|
|
|
|
vfs_init_elapsed_time += timer_reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// file
|
|
|
|
//
|
|
|
|
|
|
|
|
void stats_unique_name(size_t name_len)
|
|
|
|
{
|
|
|
|
unique_names++;
|
|
|
|
unique_name_len_total += name_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void stats_open(const char* atom_fn, size_t file_size)
|
|
|
|
{
|
|
|
|
open_files_cur++;
|
|
|
|
open_files_max = MAX(open_files_max, open_files_cur);
|
|
|
|
|
|
|
|
PairIB ret = opened_files.insert(atom_fn);
|
|
|
|
// hadn't been opened yet
|
|
|
|
if(ret.second)
|
|
|
|
opened_file_size_total += file_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stats_close()
|
|
|
|
{
|
|
|
|
debug_assert(open_files_cur > 0);
|
|
|
|
open_files_cur--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// file_buf
|
|
|
|
//
|
|
|
|
|
|
|
|
void stats_buf_alloc(size_t user_size, size_t padded_size)
|
|
|
|
{
|
|
|
|
extant_bufs_cur++;
|
|
|
|
extant_bufs_max = MAX(extant_bufs_max, extant_bufs_cur);
|
|
|
|
extant_bufs_total++;
|
|
|
|
|
|
|
|
buf_user_size_total += user_size;
|
|
|
|
buf_padded_size_total += padded_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stats_buf_free()
|
|
|
|
{
|
|
|
|
debug_assert(extant_bufs_cur > 0);
|
|
|
|
extant_bufs_cur--;
|
|
|
|
}
|
|
|
|
|
2006-02-05 00:32:01 +01:00
|
|
|
void stats_buf_ref()
|
|
|
|
{
|
|
|
|
extant_bufs_cur++;
|
|
|
|
}
|
|
|
|
|
2006-01-25 08:21:45 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// file_io
|
|
|
|
//
|
|
|
|
|
2006-03-24 22:56:00 +01:00
|
|
|
void stats_io_user_request(size_t user_size)
|
2006-01-25 08:21:45 +01:00
|
|
|
{
|
|
|
|
user_ios++;
|
|
|
|
user_io_size_total += user_size;
|
|
|
|
}
|
|
|
|
|
2006-03-24 22:56:00 +01:00
|
|
|
// these bracket file_io's IOManager::run and measure effective throughput.
|
|
|
|
// note: cannot be called from aio issue/finish because IOManager's
|
|
|
|
// decompression may cause us to miss the exact end of IO, thus throwing off
|
|
|
|
// throughput measurements.
|
|
|
|
void stats_io_sync_start(double* start_time_storage)
|
2006-01-25 08:21:45 +01:00
|
|
|
{
|
|
|
|
timer_start(start_time_storage);
|
|
|
|
}
|
|
|
|
|
2006-03-24 22:56:00 +01:00
|
|
|
void stats_io_sync_finish(FileIOImplentation fi, FileOp fo, ssize_t user_size, double* start_time_storage)
|
2006-01-25 08:21:45 +01:00
|
|
|
{
|
|
|
|
debug_assert(fi < FI_MAX_IDX);
|
|
|
|
debug_assert(fo == FO_READ || FO_WRITE);
|
|
|
|
|
2006-03-18 22:28:47 +01:00
|
|
|
// ignore IOs that failed (nothing we can do)
|
|
|
|
if(user_size > 0)
|
|
|
|
{
|
|
|
|
io_actual_size_total[fi][fo] += user_size;
|
|
|
|
io_elapsed_time[fi][fo] += timer_reset(start_time_storage);
|
|
|
|
}
|
2006-01-25 08:21:45 +01:00
|
|
|
}
|
|
|
|
|
2006-03-24 22:56:00 +01:00
|
|
|
|
|
|
|
void stats_io_check_seek(BlockId disk_pos)
|
|
|
|
{
|
|
|
|
static BlockId cur_disk_pos;
|
|
|
|
|
|
|
|
// makes debugging ("why are there seeks") a bit nicer by suppressing
|
|
|
|
// the first (bogus) seek.
|
|
|
|
if(!cur_disk_pos.atom_fn)
|
|
|
|
goto dont_count_first_seek;
|
|
|
|
|
|
|
|
if(disk_pos.atom_fn != cur_disk_pos.atom_fn || // different file OR
|
|
|
|
disk_pos.block_num != cur_disk_pos.block_num+1) // nonsequential
|
|
|
|
io_seeks++;
|
|
|
|
|
|
|
|
dont_count_first_seek:
|
|
|
|
cur_disk_pos = disk_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
void stats_cb_start()
|
|
|
|
{
|
|
|
|
timer_start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void stats_cb_finish()
|
|
|
|
{
|
|
|
|
io_process_time_total += timer_reset();
|
|
|
|
}
|
|
|
|
|
2006-01-25 08:21:45 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// file_cache
|
|
|
|
//
|
|
|
|
|
|
|
|
void stats_cache(CacheRet cr, size_t size, const char* atom_fn)
|
|
|
|
{
|
|
|
|
debug_assert(cr == CR_HIT || cr == CR_MISS);
|
|
|
|
|
|
|
|
if(cr == CR_MISS)
|
|
|
|
{
|
|
|
|
PairIB ret = ever_cached_files.insert(atom_fn);
|
|
|
|
if(!ret.second) // was already cached once
|
|
|
|
{
|
|
|
|
conflict_miss_size_total += size;
|
|
|
|
conflict_misses++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cache_count[cr]++;
|
|
|
|
cache_size_total[cr] += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stats_block_cache(CacheRet cr)
|
|
|
|
{
|
|
|
|
debug_assert(cr == CR_HIT || cr == CR_MISS);
|
|
|
|
block_cache_count[cr]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-24 22:56:00 +01:00
|
|
|
//
|
|
|
|
// archive builder
|
|
|
|
//
|
|
|
|
|
|
|
|
void stats_ab_connection(bool already_exists)
|
|
|
|
{
|
|
|
|
ab_connection_attempts++;
|
|
|
|
if(already_exists)
|
|
|
|
ab_repeated_connections++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-31 08:48:32 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2006-03-18 22:28:47 +01:00
|
|
|
template<typename T> int percent(T num, T divisor)
|
|
|
|
{
|
|
|
|
if(!divisor)
|
|
|
|
return 0;
|
|
|
|
return (int)(100*num / divisor);
|
|
|
|
}
|
|
|
|
|
2006-01-25 08:21:45 +01:00
|
|
|
void stats_dump()
|
|
|
|
{
|
2006-01-31 08:48:32 +01:00
|
|
|
const double KB = 1e3; const double MB = 1e6; const double ms = 1e-3;
|
2006-01-25 08:21:45 +01:00
|
|
|
|
2006-01-31 08:48:32 +01:00
|
|
|
debug_printf("--------------------------------------------------------------------------------\n");
|
|
|
|
debug_printf("File statistics:\n");
|
2006-01-25 08:21:45 +01:00
|
|
|
|
2006-01-31 08:48:32 +01:00
|
|
|
// note: we split the reports into several debug_printfs for clarity;
|
|
|
|
// this is necessary anyway due to fixed-size buffer.
|
2006-01-25 08:21:45 +01:00
|
|
|
|
|
|
|
debug_printf(
|
2006-03-24 22:56:00 +01:00
|
|
|
"\nvfs:\n"
|
2006-01-31 08:48:32 +01:00
|
|
|
"Total files: %u (%g MB)\n"
|
|
|
|
"Init/mount time: %g ms\n",
|
|
|
|
vfs_files, vfs_size_total/MB,
|
|
|
|
vfs_init_elapsed_time/ms
|
|
|
|
);
|
|
|
|
|
|
|
|
debug_printf(
|
2006-03-24 22:56:00 +01:00
|
|
|
"\nfile:\n"
|
2006-01-31 08:48:32 +01:00
|
|
|
"Total names: %u (%u KB)\n"
|
|
|
|
"Accessed files: %u (%g MB) -- %u%% of data set\n"
|
|
|
|
"Max. concurrent: %u; leaked: %u.\n",
|
|
|
|
unique_names, unique_name_len_total/1000,
|
2006-03-18 22:28:47 +01:00
|
|
|
opened_files.size(), opened_file_size_total/MB, percent(opened_files.size(), vfs_files),
|
2006-01-31 08:48:32 +01:00
|
|
|
open_files_max, open_files_cur
|
|
|
|
);
|
|
|
|
|
|
|
|
debug_printf(
|
2006-03-24 22:56:00 +01:00
|
|
|
"\nfile_buf:\n"
|
2006-01-31 08:48:32 +01:00
|
|
|
"Total buffers used: %u (%g MB)\n"
|
|
|
|
"Max concurrent: %u; leaked: %u\n"
|
|
|
|
"Internal fragmentation: %d%%\n",
|
|
|
|
extant_bufs_total, buf_user_size_total/MB,
|
|
|
|
extant_bufs_max, extant_bufs_cur,
|
2006-03-18 22:28:47 +01:00
|
|
|
percent(buf_padded_size_total-buf_user_size_total, buf_user_size_total)
|
2006-01-31 08:48:32 +01:00
|
|
|
);
|
|
|
|
|
2006-01-25 08:21:45 +01:00
|
|
|
debug_printf(
|
2006-03-24 22:56:00 +01:00
|
|
|
"\nfile_io:\n"
|
|
|
|
"Total user load requests: %u (%g MB)\n"
|
2006-01-25 08:21:45 +01:00
|
|
|
"IO thoughput [MB/s; 0=never happened]:\n"
|
|
|
|
" lowio: R=%.3g, W=%.3g\n"
|
|
|
|
" aio: R=%.3g, W=%.3g\n"
|
2006-03-24 22:56:00 +01:00
|
|
|
"Average size = %g KB; seeks: %u; total callback time: %g ms\n"
|
|
|
|
"Total data actually read from disk = %g MB\n",
|
2006-01-31 08:48:32 +01:00
|
|
|
user_ios, user_io_size_total/MB,
|
|
|
|
#define THROUGHPUT(impl, op) (io_elapsed_time[impl][op] == 0.0)? 0.0 : (io_actual_size_total[impl][op] / io_elapsed_time[impl][op] / MB)
|
|
|
|
THROUGHPUT(FI_LOWIO, FO_READ), THROUGHPUT(FI_LOWIO, FO_WRITE),
|
|
|
|
THROUGHPUT(FI_AIO , FO_READ), THROUGHPUT(FI_AIO , FO_WRITE),
|
2006-03-24 22:56:00 +01:00
|
|
|
user_io_size_total/user_ios/KB, io_seeks, io_process_time_total/ms,
|
|
|
|
(io_actual_size_total[FI_LOWIO][FO_READ]+io_actual_size_total[FI_AIO][FO_READ])/MB
|
2006-01-31 08:48:32 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
debug_printf(
|
2006-03-24 22:56:00 +01:00
|
|
|
"\nfile_cache:\n"
|
2006-03-25 08:08:29 +01:00
|
|
|
"Hits: %u (%g MB); misses %u (%g MB); ratio: %u%%\n"
|
|
|
|
"Percent of requested bytes satisfied by cache: %u%%; non-compulsory misses: %u (%u%% of misses)\n"
|
2006-01-31 08:48:32 +01:00
|
|
|
"Block hits: %u; misses: %u; ratio: %u%%\n",
|
2006-03-25 08:08:29 +01:00
|
|
|
cache_count[CR_HIT], cache_size_total[CR_HIT]/MB, cache_count[CR_MISS], cache_size_total[CR_MISS]/MB, percent(cache_count[CR_HIT], cache_count[CR_HIT]+cache_count[CR_MISS]),
|
|
|
|
percent(cache_size_total[CR_HIT], cache_size_total[CR_HIT]+cache_size_total[CR_MISS]), conflict_misses, percent(conflict_misses, cache_count[CR_MISS]),
|
2006-03-18 22:28:47 +01:00
|
|
|
block_cache_count[CR_HIT], block_cache_count[CR_MISS], percent(block_cache_count[CR_HIT], block_cache_count[CR_HIT]+block_cache_count[CR_MISS])
|
2006-01-31 08:48:32 +01:00
|
|
|
);
|
2006-03-24 22:56:00 +01:00
|
|
|
|
|
|
|
debug_printf(
|
|
|
|
"\nvfs_optimizer:\n"
|
|
|
|
"Total trace entries: %u; repeated connections: %u; unique files: %u\n",
|
|
|
|
ab_connection_attempts, ab_repeated_connections, ab_connection_attempts-ab_repeated_connections
|
|
|
|
);
|
2006-01-31 08:48:32 +01:00
|
|
|
}
|