[ngcore] Profiler

This commit is contained in:
Matthias Hochsteger 2019-01-03 15:54:50 +01:00
parent 678b4497c2
commit 3a1cea6cbf
8 changed files with 537 additions and 119 deletions

View File

@ -1,5 +1,5 @@
add_library(ngcore SHARED archive.cpp logging.cpp paje_trace.cpp utils.cpp)
add_library(ngcore SHARED archive.cpp logging.cpp paje_trace.cpp utils.cpp profiler.cpp)
target_compile_definitions(ngcore PRIVATE NGCORE_EXPORTS)
if(NOT WIN32)
@ -32,7 +32,7 @@ if(USE_PYTHON)
endif(USE_PYTHON)
install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp
exception.hpp symboltable.hpp paje_trace.hpp utils.hpp
exception.hpp symboltable.hpp paje_trace.hpp utils.hpp profiler.hpp
DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel)
if(ENABLE_CPP_CORE_GUIDELINES_CHECK)

View File

@ -1,9 +1,9 @@
#include <algorithm>
#include <atomic>
#include <iostream>
#include <map>
#include <set>
#include <thread>
#include <iostream>
#include "archive.hpp" // for Demangle
#include "paje_trace.hpp"
@ -23,13 +23,13 @@ namespace ngcore
PajeTrace :: PajeTrace(int anthreads, std::string aname)
{
start_time = GetTime();
start_time = GetTimeCounter();
nthreads = anthreads;
tracefile_name = aname;
tracefile_name = std::move(aname);
int bytes_per_event=33;
max_num_events_per_thread = std::min( (size_t)std::numeric_limits<int>::max, max_tracefile_size/bytes_per_event/(2*nthreads+1)*10/7);
max_num_events_per_thread = std::min( static_cast<size_t>(std::numeric_limits<int>::max()), max_tracefile_size/bytes_per_event/(2*nthreads+1)*10/7);
if(max_num_events_per_thread>0)
{
logger->info( "Tracefile size = {}MB", max_tracefile_size/1024/1024);
@ -53,7 +53,7 @@ namespace ngcore
PajeTrace :: ~PajeTrace()
{
if(tracefile_name.size()>0)
if(!tracefile_name.empty())
Write(tracefile_name);
}
@ -67,11 +67,9 @@ namespace ngcore
tracing_enabled = false;
}
using std::string;
class PajeFile
{
public:
typedef PajeTrace::TTimePoint TTimePoint;
static void Hue2RGB ( double x, double &r, double &g, double &b )
{
double d = 1.0/6.0;
@ -139,36 +137,35 @@ namespace ngcore
bool value_is_alias = true;
bool operator < (const PajeEvent & other) const {
// Same times can occur for very small tasks -> take "starting" events first (eg. PajePushState before PajePopState)
// Same start and stop times can occur for very small tasks -> take "starting" events first (eg. PajePushState before PajePopState)
if(time == other.time)
return event_type < other.event_type;
else
return (time < other.time);
return (time < other.time);
}
int write(char *buf)
int write(FILE *stream)
{
const int &key = id;
const int &end_container = start_container;
switch(event_type)
{
case PajeSetVariable:
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeSetVariable, time, type, container, var_value );
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeSetVariable, time, type, container, var_value ); // NOLINT
case PajeAddVariable:
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeAddVariable, time, type, container, var_value );
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeAddVariable, time, type, container, var_value ); // NOLINT
case PajeSubVariable:
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeSubVariable, time, type, container, var_value );
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeSubVariable, time, type, container, var_value ); // NOLINT
case PajePushState:
if(value_is_alias)
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\ta%d\t%d\n", PajePushState, time, type, container, value, id);
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\ta%d\t%d\n", PajePushState, time, type, container, value, id); // NOLINT
else
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\t%d\t%d\n", PajePushState, time, type, container, value, id);
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\t%d\n", PajePushState, time, type, container, value, id); // NOLINT
case PajePopState:
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\n", PajePopState, time, type, container );
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\n", PajePopState, time, type, container ); // NOLINT
case PajeStartLink:
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\t%d\ta%d\t%d\n", PajeStartLink, time, type, container, value, start_container, key );
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\ta%d\t%d\n", PajeStartLink, time, type, container, value, start_container, key ); // NOLINT
case PajeEndLink:
return sprintf( buf, "%d\t%.15g\ta%d\ta%d\t%d\ta%d\t%d\n", PajeEndLink, time, type, container, value, end_container, key );
return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\ta%d\t%d\n", PajeEndLink, time, type, container, value, end_container, key ); // NOLINT
}
return 0;
}
@ -177,34 +174,46 @@ namespace ngcore
std::vector<PajeEvent> events;
public:
PajeFile( string filename, TTimePoint astart_time )
PajeFile() = delete;
PajeFile(const PajeFile &) = delete;
PajeFile(PajeFile &&) = delete;
void operator=(const PajeFile &) = delete;
void operator=(PajeFile &&) = delete;
PajeFile( const std::string & filename, TTimePoint astart_time )
{
start_time = astart_time;
ctrace_stream = fopen (filename.c_str(),"w");
fprintf(ctrace_stream, "%s", header );
ctrace_stream = fopen (filename.c_str(),"w"); // NOLINT
fprintf(ctrace_stream, "%s", header ); // NOLINT
alias_counter = 0;
}
int DefineContainerType ( int parent_type, string name )
~PajeFile()
{
fclose (ctrace_stream); // NOLINT
}
int DefineContainerType ( int parent_type, const std::string & name )
{
int alias = ++alias_counter;
if(parent_type!=0)
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\n", PajeDefineContainerType, alias, parent_type, name.c_str() );
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\n", PajeDefineContainerType, alias, parent_type, name.c_str() ); // NOLINT
else
fprintf( ctrace_stream, "%d\ta%d\t%d\t\"%s\"\n", PajeDefineContainerType, alias, parent_type, name.c_str() );
fprintf( ctrace_stream, "%d\ta%d\t%d\t\"%s\"\n", PajeDefineContainerType, alias, parent_type, name.c_str() ); // NOLINT
return alias;
}
int DefineVariableType ( int container_type, string name )
int DefineVariableType ( int container_type, const std::string & name )
{
int alias = ++alias_counter;
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"1.0 1.0 1.0\"\n", PajeDefineVariableType, alias, container_type, name.c_str() );
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"1.0 1.0 1.0\"\n", PajeDefineVariableType, alias, container_type, name.c_str() ); // NOLINT
return alias;
}
int DefineStateType ( int type, string name )
int DefineStateType ( int type, const std::string & name )
{
int alias = ++alias_counter;
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\n", PajeDefineStateType, alias, type, name.c_str() );
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\n", PajeDefineStateType, alias, type, name.c_str() ); // NOLINT
return alias;
}
@ -213,38 +222,38 @@ namespace ngcore
// Write("event not implemented");
// }
int DefineLinkType (int parent_container_type, int start_container_type, int stop_container_type, string name)
int DefineLinkType (int parent_container_type, int start_container_type, int stop_container_type, const std::string & name)
{
int alias = ++alias_counter;
fprintf( ctrace_stream, "%d\ta%d\ta%d\ta%d\ta%d\t\"%s\"\n", PajeDefineLinkType, alias, parent_container_type, start_container_type, stop_container_type, name.c_str() );
fprintf( ctrace_stream, "%d\ta%d\ta%d\ta%d\ta%d\t\"%s\"\n", PajeDefineLinkType, alias, parent_container_type, start_container_type, stop_container_type, name.c_str() ); // NOLINT
return alias;
}
int DefineEntityValue (int type, string name, double hue = -1)
int DefineEntityValue (int type, const std::string & name, double hue = -1)
{
if(hue==-1)
{
std::hash<string> shash;
std::hash<std::string> shash;
size_t h = shash(name);
h ^= h>>32;
h = (uint32_t)h;
h ^= h>>32U;
h = static_cast<uint32_t>(h);
hue = h*1.0/std::numeric_limits<uint32_t>::max();
}
int alias = ++alias_counter;
double r,g,b;
Hue2RGB( hue, r, g, b );
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"%.15g %.15g %.15g\"\n", PajeDefineEntityValue, alias, type, name.c_str(), r,g,b );
fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"%.15g %.15g %.15g\"\n", PajeDefineEntityValue, alias, type, name.c_str(), r,g,b ); // NOLINT
return alias;
}
int CreateContainer ( int type, int parent, string name )
int CreateContainer ( int type, int parent, const std::string & name )
{
int alias = ++alias_counter;
if(parent!=0)
fprintf( ctrace_stream, "%d\t0\ta%d\ta%d\ta%d\t\"%s\"\n", PajeCreateContainer, alias, type, parent, name.c_str() );
fprintf( ctrace_stream, "%d\t0\ta%d\ta%d\ta%d\t\"%s\"\n", PajeCreateContainer, alias, type, parent, name.c_str() ); // NOLINT
else
fprintf( ctrace_stream, "%d\t0\ta%d\ta%d\t%d\t\"%s\"\n", PajeCreateContainer, alias, type, parent, name.c_str() );
fprintf( ctrace_stream, "%d\t0\ta%d\ta%d\t%d\t\"%s\"\n", PajeCreateContainer, alias, type, parent, name.c_str() ); // NOLINT
return alias;
}
void DestroyContainer ()
@ -252,17 +261,17 @@ namespace ngcore
void SetVariable (TTimePoint time, int type, int container, double value )
{
events.push_back( PajeEvent( PajeSetVariable, ConvertTime(time), type, container, value ) );
events.emplace_back( PajeEvent( PajeSetVariable, ConvertTime(time), type, container, value ) );
}
void AddVariable (TTimePoint time, int type, int container, double value )
{
events.push_back( PajeEvent( PajeAddVariable, ConvertTime(time), type, container, value ) );
events.emplace_back( PajeEvent( PajeAddVariable, ConvertTime(time), type, container, value ) );
}
void SubVariable (TTimePoint time, int type, int container, double value )
{
events.push_back( PajeEvent( PajeSubVariable, ConvertTime(time), type, container, value ) );
events.emplace_back( PajeEvent( PajeSubVariable, ConvertTime(time), type, container, value ) );
}
void SetState ()
@ -270,12 +279,12 @@ namespace ngcore
void PushState ( TTimePoint time, int type, int container, int value, int id = 0, bool value_is_alias = true )
{
events.push_back( PajeEvent( PajePushState, ConvertTime(time), type, container, value, id) );
events.emplace_back( PajeEvent( PajePushState, ConvertTime(time), type, container, value, id, value_is_alias) );
}
void PopState ( TTimePoint time, int type, int container )
{
events.push_back( PajeEvent( PajePopState, ConvertTime(time), type, container ) );
events.emplace_back( PajeEvent( PajePopState, ConvertTime(time), type, container ) );
}
void ResetState ()
@ -283,12 +292,12 @@ namespace ngcore
void StartLink ( TTimePoint time, int type, int container, int value, int start_container, int key )
{
events.push_back( PajeEvent( PajeStartLink, ConvertTime(time), type, container, value, start_container, key ) );
events.emplace_back( PajeEvent( PajeStartLink, ConvertTime(time), type, container, value, start_container, key ) );
}
void EndLink ( TTimePoint time, int type, int container, int value, int end_container, int key )
{
events.push_back( PajeEvent( PajeEndLink, ConvertTime(time), type, container, value, end_container, key ) );
events.emplace_back( PajeEvent( PajeEndLink, ConvertTime(time), type, container, value, end_container, key ) );
}
void NewEvent ()
@ -299,12 +308,11 @@ namespace ngcore
logger->info("Sorting traces...");
std::sort (events.begin(), events.end());
char buf[2*MAX_TRACE_LINE_SIZE];
logger->info("Writing traces... ");
for (int i = 0; i < events.size(); i++)
for (auto & event : events)
{
events[i].write( buf );
fprintf( ctrace_stream, "%s", buf );
event.write( ctrace_stream );
// fprintf( ctrace_stream, "%s", buf ); // NOLINT
}
logger->info("Done");
}
@ -336,7 +344,7 @@ namespace ngcore
NGCORE_API PajeTrace *trace;
void PajeTrace::Write( string filename )
void PajeTrace::Write( const std::string & filename )
{
int n_events = jobs.size() + timer_events.size();
for(auto & vtasks : tasks)
@ -352,7 +360,7 @@ namespace ngcore
if(!tracing_enabled)
{
logger->warning("Tracing stopped during computation due to tracefile size limit of {} megabytes.", max_tracefile_size/1024/1024);
logger->warn("Tracing stopped during computation due to tracefile size limit of {} megabytes.", max_tracefile_size/1024/1024);
}
PajeFile paje(filename, start_time);
@ -374,18 +382,19 @@ namespace ngcore
paje.SetVariable( start_time, variable_type_active_threads, container_jobs, 0.0 );
const int num_nodes = 1; //task_manager ? task_manager->GetNumNodes() : 1;
std::vector<int> container_nodes;
std::vector<int> container_nodes;
container_nodes.reserve(num_nodes);
for(int i=0; i<num_nodes; i++)
container_nodes.push_back( paje.CreateContainer( container_type_node, container_task_manager, "Node " /* TODO: + ToString(i) */));
container_nodes.emplace_back( paje.CreateContainer( container_type_node, container_task_manager, "Node " + ToString(i)) );
std::vector <int> thread_aliases;
thread_aliases.reserve(nthreads);
if(trace_threads)
for (int i=0; i<nthreads; i++)
{
char name[20];
sprintf(name, "Thread %d", i);
thread_aliases.push_back( paje.CreateContainer( container_type_thread, container_nodes[i*num_nodes/nthreads], name ) );
auto name = "Timer level " + ToString(i);
thread_aliases.emplace_back( paje.CreateContainer( container_type_thread, container_nodes[i*num_nodes/nthreads], name ) );
}
std::map<const std::type_info *, int> job_map;
@ -394,7 +403,7 @@ namespace ngcore
for(Job & j : jobs)
if(job_map.find(j.type) == job_map.end())
{
string name = Demangle(j.type->name());
std::string name = Demangle(j.type->name());
job_map[j.type] = paje.DefineEntityValue( state_type_job, name, -1 );
job_task_map[j.type] = paje.DefineEntityValue( state_type_task, name, -1 );
}
@ -437,8 +446,7 @@ namespace ngcore
timer_container_aliases.resize(maxdepth);
for(int i=0; i<maxdepth; i++)
{
char name[30];
sprintf(name, "Timer level %d", i);
auto name = "Timer level " + ToString(i);
timer_container_aliases[i] = paje.CreateContainer( container_type_timer, container_task_manager, name );
}
@ -497,10 +505,10 @@ namespace ngcore
while(nlinks_merged < nlinks)
{
int minpos = -1;
TTimePoint mintime;
TTimePoint mintime = -1;
for (int t = 0; t<nthreads; t++)
{
if(pos[t] < links[t].size() && (links[t][pos[t]].time < mintime || minpos==-1))
if(pos[t] < links[t].size() && (minpos==-1 || links[t][pos[t]].time < mintime))
{
minpos = t;
mintime = links[t][pos[t]].time;
@ -544,7 +552,7 @@ namespace ngcore
}
paje.WriteEvents();
}
}
} // namespace ngcore
const char *header =
"%EventDef PajeDefineContainerType 0 \n"

View File

@ -5,8 +5,9 @@
#include <vector>
#include <x86intrin.h> // for __rdtsc() CPU time step counter
#include "ngcore_api.hpp" // for NGCORE_API
#include "logging.hpp" // for logger
#include "ngcore_api.hpp" // for NGCORE_API
#include "utils.hpp"
namespace ngcore
{
@ -15,15 +16,11 @@ namespace ngcore
class PajeTrace
{
public:
typedef std::chrono::system_clock TClock;
// typedef TClock::time_point TTimePoint;
typedef size_t TTimePoint;
using TClock = std::chrono::system_clock;
protected:
std::shared_ptr<spdlog::logger> logger = GetLogger("PajeTrace");
private:
friend class TraceDisabler;
NGCORE_API static size_t max_tracefile_size;
static bool trace_thread_counter;
static bool trace_threads;
@ -104,24 +101,24 @@ namespace ngcore
std::vector<TimerEvent> timer_events;
std::vector<std::vector<ThreadLink> > links;
TTimePoint GetTime()
{
// return TClock::now();
return TTimePoint(__rdtsc());
}
public:
NGCORE_API void StopTracing();
PajeTrace() = delete;
PajeTrace(const PajeTrace &) = delete;
PajeTrace(PajeTrace &&) = delete;
PajeTrace(int anthreads, std::string aname = "");
~PajeTrace();
void operator=(const PajeTrace &) = delete;
void operator=(PajeTrace &&) = delete;
void StartTimer(int timer_id)
{
if(!tracing_enabled) return;
if(unlikely(timer_events.size() == max_num_events_per_thread))
StopTracing();
timer_events.push_back(TimerEvent{timer_id, GetTime(), true});
timer_events.push_back(TimerEvent{timer_id, GetTimeCounter(), true});
}
void StopTimer(int timer_id)
@ -129,7 +126,7 @@ namespace ngcore
if(!tracing_enabled) return;
if(unlikely(timer_events.size() == max_num_events_per_thread))
StopTracing();
timer_events.push_back(TimerEvent{timer_id, GetTime(), false});
timer_events.push_back(TimerEvent{timer_id, GetTimeCounter(), false});
}
NETGEN_INLINE int StartTask(int thread_id, int id, int id_type = Task::ID_NONE, int additional_value = -1)
@ -139,7 +136,7 @@ namespace ngcore
if(unlikely(tasks[thread_id].size() == max_num_events_per_thread))
StopTracing();
int task_num = tasks[thread_id].size();
tasks[thread_id].push_back( Task{thread_id, id, id_type, additional_value, GetTime()} );
tasks[thread_id].push_back( Task{thread_id, id, id_type, additional_value, GetTimeCounter()} );
return task_num;
}
@ -147,7 +144,7 @@ namespace ngcore
{
if(!trace_threads && !trace_thread_counter) return;
if(task_num>=0)
tasks[thread_id][task_num].stop_time = GetTime();
tasks[thread_id][task_num].stop_time = GetTimeCounter();
}
void SetTask(int thread_id, int task_num, int additional_value) {
@ -161,13 +158,13 @@ namespace ngcore
if(!tracing_enabled) return;
if(jobs.size() == max_num_events_per_thread)
StopTracing();
jobs.push_back( Job{job_id, &type, GetTime()} );
jobs.push_back( Job{job_id, &type, GetTimeCounter()} );
}
void StopJob()
{
if(tracing_enabled)
jobs.back().stop_time = GetTime();
jobs.back().stop_time = GetTimeCounter();
}
void StartLink(int thread_id, int key)
@ -175,7 +172,7 @@ namespace ngcore
if(!tracing_enabled) return;
if(links[thread_id].size() == max_num_events_per_thread)
StopTracing();
links[thread_id].push_back( ThreadLink{thread_id, key, GetTime(), true} );
links[thread_id].push_back( ThreadLink{thread_id, key, GetTimeCounter(), true} );
}
void StopLink(int thread_id, int key)
@ -183,33 +180,12 @@ namespace ngcore
if(!tracing_enabled) return;
if(links[thread_id].size() == max_num_events_per_thread)
StopTracing();
links[thread_id].push_back( ThreadLink{thread_id, key, GetTime(), false} );
links[thread_id].push_back( ThreadLink{thread_id, key, GetTimeCounter(), false} );
}
void Write( std::string filename );
void Write( const std::string & filename );
};
class TraceDisabler
{
bool trace_thread_counter;
bool trace_threads;
public:
TraceDisabler()
{
trace_thread_counter = PajeTrace::trace_thread_counter;
PajeTrace::trace_thread_counter = false;
trace_threads = PajeTrace::trace_threads;
PajeTrace::trace_threads = false;
}
~TraceDisabler()
{
PajeTrace::trace_thread_counter = trace_thread_counter;
PajeTrace::trace_threads = trace_threads;
}
};
}
} // namespace ngcore
#endif // NETGEN_CORE_PAJE_TRACE_HPP

106
libsrc/core/profiler.cpp Normal file
View File

@ -0,0 +1,106 @@
#include <mutex>
#include "profiler.hpp"
namespace ngcore
{
std::array<NgProfiler::TimerVal,NgProfiler::SIZE> NgProfiler::timers; // NOLINT
std::string NgProfiler::filename;
size_t dummy_thread_times[NgProfiler::SIZE];
size_t * NgProfiler::thread_times = dummy_thread_times; // NOLINT
size_t dummy_thread_flops[NgProfiler::SIZE];
size_t * NgProfiler::thread_flops = dummy_thread_flops; // NOLINT
std::shared_ptr<spdlog::logger> logger = GetLogger("Profiler"); // NOLINT
NgProfiler :: NgProfiler()
{
for (auto & t : timers)
{
t.tottime = 0.0;
t.usedcounter = 0;
t.flops = 0.0;
}
}
NgProfiler :: ~NgProfiler()
{
if (filename.length())
{
logger->debug( "write profile to file {}", filename );
FILE *prof = fopen(filename.c_str(),"w"); // NOLINT
Print (prof);
fclose(prof); // NOLINT
}
}
void NgProfiler :: Print (FILE * prof)
{
int i = 0;
for (auto & t : timers)
{
if (t.count != 0 || t.usedcounter != 0)
{
fprintf(prof,"job %3i calls %8li, time %6.4f sec",i,t.count,t.tottime); // NOLINT
if(t.flops != 0.0)
fprintf(prof,", MFlops = %6.2f",t.flops / (t.tottime) * 1e-6); // NOLINT
if(t.loads != 0.0)
fprintf(prof,", MLoads = %6.2f",t.loads / (t.tottime) * 1e-6); // NOLINT
if(t.stores != 0.0)
fprintf(prof,", MStores = %6.2f",t.stores / (t.tottime) * 1e-6); // NOLINT
if(t.usedcounter)
fprintf(prof," %s",t.name.c_str()); // NOLINT
fprintf(prof,"\n"); // NOLINT
}
i++;
}
}
int NgProfiler :: CreateTimer (const std::string & name)
{
static std::mutex createtimer_mutex;
int nr = -1;
{
std::lock_guard<std::mutex> guard(createtimer_mutex);
for (int i = SIZE-1; i > 0; i--)
{
auto & t = timers[i];
if (!t.usedcounter)
{
t.usedcounter = 1;
t.name = name;
nr = i;
break;
}
}
}
if (nr > -1) return nr;
static bool first_overflow = true;
if (first_overflow)
{
first_overflow = false;
NgProfiler::logger->warn("no more timer available, reusing last one");
}
return 0;
}
void NgProfiler :: Reset ()
{
for(auto & t : timers)
{
t.tottime = 0.0;
t.count = 0;
t.flops = 0.0;
t.loads = 0;
t.stores = 0;
}
}
NgProfiler prof; // NOLINT
} // namespace ngcore

286
libsrc/core/profiler.hpp Normal file
View File

@ -0,0 +1,286 @@
#ifndef NETGEN_CORE_PROFILER_HPP
#define NETGEN_CORE_PROFILER_HPP
#include <chrono>
#include <string>
#include "logging.hpp"
#include "paje_trace.hpp"
#include "utils.hpp"
namespace ngcore
{
class NgProfiler
{
public:
/// maximal number of timers
enum { SIZE = 8*1024 };
struct TimerVal
{
TimerVal() = default;
double tottime = 0.0;
double starttime = 0.0;
double flops = 0.0;
double loads = 0.0;
double stores = 0.0;
long count = 0;
std::string name = "";
int usedcounter = 0;
};
NGCORE_API static std::array<TimerVal,SIZE> timers;
NGCORE_API static TTimePoint * thread_times;
NGCORE_API static TTimePoint * thread_flops;
NGCORE_API static std::shared_ptr<spdlog::logger> logger;
private:
static std::string filename;
public:
NgProfiler();
~NgProfiler();
NgProfiler(const NgProfiler &) = delete;
NgProfiler(NgProfiler &&) = delete;
void operator=(const NgProfiler &) = delete;
void operator=(NgProfiler &&) = delete;
static void SetFileName (const std::string & afilename) { filename = afilename; }
/// create new timer, use integer index
NGCORE_API static int CreateTimer (const std::string & name);
NGCORE_API static void Reset ();
/// start timer of index nr
static void StartTimer (int nr)
{
timers[nr].starttime = WallTime(); timers[nr].count++;
}
/// stop timer of index nr
static void StopTimer (int nr)
{
timers[nr].tottime += WallTime()-timers[nr].starttime;
}
static void StartThreadTimer (size_t nr, size_t tid)
{
thread_times[tid*SIZE+nr] -= GetTimeCounter();
}
static void StopThreadTimer (size_t nr, size_t tid)
{
thread_times[tid*SIZE+nr] += GetTimeCounter();
}
static void AddThreadFlops (size_t nr, size_t tid, size_t flops)
{
thread_flops[tid*SIZE+nr] += flops;
}
/// if you know number of flops, provide them to obtain the MFlop - rate
static void AddFlops (int nr, double aflops) { timers[nr].flops += aflops; }
static void AddLoads (int nr, double aloads) { timers[nr].loads += aloads; }
static void AddStores (int nr, double astores) { timers[nr].stores += astores; }
static int GetNr (const std::string & name)
{
for (int i = SIZE-1; i >= 0; i--)
if (timers[i].name == name)
return i;
return -1;
}
static double GetTime (int nr)
{
return timers[nr].tottime;
}
static double GetTime (const std::string & name)
{
for (int i = SIZE-1; i >= 0; i--)
if (timers[i].name == name)
return GetTime (i);
return 0;
}
static long int GetCounts (int nr)
{
return timers[nr].count;
}
static double GetFlops (int nr)
{
return timers[nr].flops;
}
/// change name
static void SetName (int nr, const std::string & name) { timers[nr].name = name; }
static std::string GetName (int nr) { return timers[nr].name; }
/// print profile
NGCORE_API static void Print (FILE * ost);
};
class Timer
{
int timernr;
int priority;
public:
Timer (const std::string & name, int apriority = 1)
: priority(apriority)
{
timernr = NgProfiler::CreateTimer (name);
}
void SetName (const std::string & name)
{
NgProfiler::SetName (timernr, name);
}
void Start ()
{
if (priority <= 2)
NgProfiler::StartTimer (timernr);
if (priority <= 1)
if(trace) trace->StartTimer(timernr);
}
void Stop ()
{
if (priority <= 2)
NgProfiler::StopTimer (timernr);
if (priority <= 1)
if(trace) trace->StopTimer(timernr);
}
void AddFlops (double aflops)
{
if (priority <= 2)
NgProfiler::AddFlops (timernr, aflops);
}
double GetTime () { return NgProfiler::GetTime(timernr); }
long int GetCounts () { return NgProfiler::GetCounts(timernr); }
double GetMFlops ()
{ return NgProfiler::GetFlops(timernr)
/ NgProfiler::GetTime(timernr) * 1e-6; }
operator int () { return timernr; }
};
/**
Timer object.
Start / stop timer at constructor / destructor.
*/
class RegionTimer
{
Timer & timer;
public:
/// start timer
RegionTimer (Timer & atimer) : timer(atimer) { timer.Start(); }
/// stop timer
~RegionTimer () { timer.Stop(); }
RegionTimer() = delete;
RegionTimer(RegionTimer &&) = delete;
RegionTimer(const RegionTimer &) = delete;
void operator=(const RegionTimer &) = delete;
void operator=(RegionTimer &&) = delete;
};
class ThreadRegionTimer
{
size_t nr;
size_t tid;
public:
/// start timer
ThreadRegionTimer (size_t _nr, size_t _tid) : nr(_nr), tid(_tid)
{ NgProfiler::StartThreadTimer(nr, tid); }
/// stop timer
~ThreadRegionTimer ()
{ NgProfiler::StopThreadTimer(nr, tid); }
ThreadRegionTimer() = delete;
ThreadRegionTimer(ThreadRegionTimer &&) = delete;
ThreadRegionTimer(const ThreadRegionTimer &) = delete;
void operator=(const ThreadRegionTimer &) = delete;
void operator=(ThreadRegionTimer &&) = delete;
};
class RegionTracer
{
int nr;
int thread_id;
public:
static constexpr int ID_JOB = PajeTrace::Task::ID_JOB;
static constexpr int ID_NONE = PajeTrace::Task::ID_NONE;
static constexpr int ID_TIMER = PajeTrace::Task::ID_TIMER;
RegionTracer() = delete;
RegionTracer(RegionTracer &&) = delete;
RegionTracer(const RegionTracer &) = delete;
void operator=(const RegionTracer &) = delete;
void operator=(RegionTracer &&) = delete;
/// start trace
RegionTracer (int athread_id, int region_id, int id_type = ID_NONE, int additional_value = -1 )
: thread_id(athread_id)
{
if (trace)
nr = trace->StartTask (athread_id, region_id, id_type, additional_value);
}
/// start trace with timer
RegionTracer (int athread_id, Timer & timer, int additional_value = -1 )
: thread_id(athread_id)
{
if (trace)
nr = trace->StartTask (athread_id, static_cast<int>(timer), ID_TIMER, additional_value);
}
/// set user defined value
void SetValue( int additional_value )
{
if (trace)
trace->SetTask( thread_id, nr, additional_value );
}
/// stop trace
~RegionTracer ()
{
if (trace)
trace->StopTask (thread_id, nr);
}
};
// Helper function for timings
// Run f() at least min_iterations times until max_time seconds elapsed
// returns minimum runtime for a call of f()
template<typename TFunc>
double RunTiming( TFunc f, double max_time = 0.5, int min_iterations = 10 )
{
// Make sure the whole test run does not exceed maxtime
double tend = WallTime()+max_time;
// warmup
f();
double tres = std::numeric_limits<double>::max();
int iteration = 0;
while(WallTime()<tend || iteration++ < min_iterations)
{
double t = -WallTime();
f();
t += WallTime();
tres = std::min(tres, t);
}
return tres;
}
} // namespace ngcore
#endif // NETGEN_CORE_PROFILER_HPP

View File

@ -1,8 +1,10 @@
#include "utils.hpp"
#include "logging.hpp"
#ifndef WIN32
#include <cxxabi.h>
#endif
#include <iostream>
namespace ngcore
{
@ -14,6 +16,24 @@ namespace ngcore
nullptr,
nullptr,
&status); }
double ticks_per_second = [] () noexcept
{
auto tick_start = GetTimeCounter();
double tstart = WallTime();
double tend = WallTime()+0.001;
// wait for 1ms and compare wall time with time counter
while(WallTime()<tend);
auto tick_end = GetTimeCounter();
tend = WallTime();
return (tick_end-tick_start)/(tend-tstart);
}();
const std::chrono::time_point<TClock> wall_time_start = TClock::now();
} // namespace ngcore
#endif

View File

@ -1,8 +1,10 @@
#ifndef NETGEN_CORE_UTILS_HPP
#define NETGEN_CORE_UTILS_HPP
#include <string>
#include <chrono>
#include <sstream>
#include <string>
#include <x86intrin.h> // for __rdtsc() CPU time step counter
#include "ngcore_api.hpp" // for NGCORE_API
@ -18,6 +20,35 @@ namespace ngcore
inline bool unlikely (bool x) { return x; }
#endif
using TClock = std::chrono::system_clock;
extern NGCORE_API const std::chrono::time_point<TClock> wall_time_start;
// Time in seconds since program start
inline double WallTime () noexcept
{
std::chrono::time_point<TClock> now = TClock::now();
std::chrono::duration<double> elapsed_seconds = now-wall_time_start;
return elapsed_seconds.count();
}
// High precision clock counter register
using TTimePoint = size_t;
extern NGCORE_API double ticks_per_second;
inline TTimePoint GetTimeCounter() noexcept
{
return TTimePoint(__rdtsc());
}
template <class T>
inline std::string ToString (const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
} // namespace ngcore
#endif // NETGEN_CORE_UTILS_HPP

View File

@ -66,16 +66,7 @@ namespace netgen
return static_cast<typename function_traits<Function>::pointer>(lambda);
}
template <class T>
inline std::string ToString (const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
}
} // namespace netgen
#endif