{
public:
static int64 clock()
{
int64 begin;
QueryPerformanceCounter( (LARGE_INTEGER*)&begin );
return begin;
}
static int64 clockpersec()
{
int64 freq;
QueryPerformanceCounter( (LARGE_INTEGER*)&freq );
return freq;
}
};
class stat_collector
{
public:
stat_collector() { _freq = (float)syscore::clockpersec(); }
void flush();
uint32 enroll( const string& name, uint type );
void start( uint index );
void stop( uint index );
float mean_stat( uint index );
float inst_stat( uint index );
float total_stat( uint index );
int sample_count( uint index );
class block_stat
{
public:
block_stat( stat_collector& collector, uint index )
:_collector( &collector ), _index( index )
{
_collector->start( _index );
}
~block_stat()
{
_collector->stop( _index );
}
private:
stat_collector* _collector;
uint _index;
};
enum stat_type
{
STAT_TIME,
STAT_COUNT,
};
private:
struct stat_item
{
stat_item( const string& name, uint type )
: _count(0), _name(name), _type(type)
{}
uint _count;
string _name;
uint _type;
};
std::vector<sint64> _prv_stats;
std::vector<sint64> _cur_stats;
std::vector<stat_item> _stat_info;
float _freq;
};
void stat_collector::flush()
{
//_prv_stats = _cur_stats;
}
uint32 stat_collector::enroll( const string& name, uint type )
{
assert( _prv_stats.size() == _cur_stats.size() );
assert( _prv_stats.size() == _names.size() );
uint32 index = _cur_stats.size();
_prv_stats.push_back( 0 );
_cur_stats.push_back( 0 );
_stat_info.push_back( stat_item( name, type ) );
return index;
}
void stat_collector::start( uint index )
{
_prv_stats[index] = _cur_stats[index];
switch ( _stat_info[index]._type )
{
case STAT_TIME:
_cur_stats[index] -= syscore::clock();;
break;
case STAT_COUNT:
break;
default:
assert( "unkown stat type!!!!!!!!!" && 0 );
};
}
void stat_collector::stop( uint index )
{
switch ( _stat_info[index]._type )
{
case STAT_TIME:
_cur_stats[index] += syscore::clock();
break;
case STAT_COUNT:
_cur_stats[index]++;
break;
default:
assert( "unkown stat type!!!!!!!!!" && 0 );
}
_stat_info[index]._count++;
}
float stat_collector::mean_stat( uint index )
{
switch ( _stat_info[index]._type )
{
case STAT_TIME:
return _cur_stats[index]/(_freq * _stat_info[index]._count);
case STAT_COUNT:
return (float)_cur_stats[index]/_stat_info[index]._count;
default:
assert( "unkown stat type!!!!!!!!!" && 0 );
return 0;
}
}
float stat_collector::inst_stat( uint index )
{
switch ( _stat_info[index]._type )
{
case STAT_TIME:
return (_cur_stats[index]-_prv_stats[index])/_freq;
case STAT_COUNT:
return (float)_cur_stats[index]-_prv_stats[index];
default:
assert( "unkown stat type!!!!!!!!!" && 0 );
return 0;
}
}
네이밍 컨벤션은.. C/C++ 표준 위원회의 스타일을 따라가 봤다.
사용법은,
TEST( l = c.length() ) \
TEST( c = vector_t::cross( a, b ) ) \
TEST( l = vector_t::dot( b, c ) ) \
stat_collector _stats;
template <class vector_t>
void test_run( const char* filename, uint stat_index )
{
static vector_t a, b, c;
std::ofstream log( filename, ios::app );
#define TEST( expression ) #expression "\n"
log << "=======================================" << endl;
log << TEST_LIST();
log << "---------------------------------------" << endl;
#undef TEST
for ( int count = 0; count < 10; ++count )
{
vector_t::scalar_type x = (float)rand();
vector_t::scalar_type y = (float)rand();
vector_t::scalar_type z = (float)rand();
volatile vector_t::scalar_type l = 0;
a = vector_t( x, y, 0 );
b = vector_t( -y, x, 0 );
c = vector_t( x, y, z );
_stats.start( stat_index );
for ( int i = 0; i < 10000000; ++i )
{
#define TEST( expression ) expression;
TEST_LIST();
#undef TEST
}
_stats.stop( stat_index );
log << _stats.inst_stat(stat_index) << std::endl;
}
log << "======== " << _stats.mean_stat(stat_index) << " sec/sample ==========" << std::endl;
}
int WINAPI WinMain( HINSTANCE , HINSTANCE, char* , int )
{
uint imp_stat, exp_stat;
imp_stat = _stats.enroll( _t("imp"), stat_collector::STAT_TIME );
exp_stat = _stats.enroll( _t("exp"), stat_collector::STAT_TIME );
test_run<vector3def>( "log_imp.txt", imp_stat );
test_run<vector3sse>( "log_exp.txt", exp_stat );
return 0;
}
이름과 타입을 등록하고, 측정할 구간을 start/end로 지정하면 끝. TEST_LIST는 테스트 대상이 어떤 코드였는지에 대한 로그를 남기기 위한 매크로.
부족한 부분은 나중에 수정하자.