Commit 4d66b3e7 authored by twanvl's avatar twanvl

Split script profiler into a separate file

parent 458af56f
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <cli/cli_main.hpp> #include <cli/cli_main.hpp>
#include <cli/text_io_handler.hpp> #include <cli/text_io_handler.hpp>
#include <script/functions/functions.hpp> #include <script/functions/functions.hpp>
#include <script/profiler.hpp>
#include <data/format/formats.hpp> #include <data/format/formats.hpp>
#include <wx/process.h> #include <wx/process.h>
...@@ -163,7 +164,11 @@ void CLISetInterface::handleCommand(const String& command) { ...@@ -163,7 +164,11 @@ void CLISetInterface::handleCommand(const String& command) {
} }
#if USE_SCRIPT_PROFILING #if USE_SCRIPT_PROFILING
} else if (before == _(":profile")) { } else if (before == _(":profile")) {
showProfilingStats(); if (arg == _("full")) {
showProfilingStats(profile_root);
} else {
showProfilingStats(profile_aggregated());
}
#endif #endif
} else { } else {
cli.showError(_("Unknown command, type :help for help.")); cli.showError(_("Unknown command, type :help for help."));
...@@ -193,15 +198,21 @@ void CLISetInterface::handleCommand(const String& command) { ...@@ -193,15 +198,21 @@ void CLISetInterface::handleCommand(const String& command) {
} }
#if USE_SCRIPT_PROFILING #if USE_SCRIPT_PROFILING
DECLARE_TYPEOF_COLLECTION(FunctionProfileItem); DECLARE_TYPEOF_COLLECTION(FunctionProfileP);
void CLISetInterface::showProfilingStats() { void CLISetInterface::showProfilingStats(const FunctionProfile& item, int level) {
vector<FunctionProfileItem> stats; // show parent
get_profile(stats); if (level == 0) {
sort(stats.begin(), stats.end()); cli << GRAY << _("Time(s) Avg (ms) Calls Function") << ENDL;
cli << GRAY << _("Time(s) Avg (ms) Calls Function") << ENDL; cli << _("======== ======== ====== ===============================") << NORMAL << ENDL;
cli << _("======== ======== ====== ===============================") << NORMAL << ENDL; } else {
FOR_EACH_REVERSE(s, stats) { for (int i = 1 ; i < level ; ++i) cli << _(" ");
cli << String::Format(_("%8.5f %8.5f %6d %s"), s.time, 1000 * s.time / s.calls, s.calls, s.name.c_str()) << ENDL; cli << String::Format(_("%8.5f %8.5f %6d %s"), item.time(), 1000 * item.avg_time(), item.calls, item.name.c_str()) << ENDL;
}
// show children
vector<FunctionProfileP> children;
item.get_children(children);
FOR_EACH_REVERSE(c, children) {
showProfilingStats(*c, level + 1);
} }
} }
#endif #endif
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <data/set.hpp> #include <data/set.hpp>
#include <data/export_template.hpp> #include <data/export_template.hpp>
#include <script/profiler.hpp>
// ----------------------------------------------------------------------------- : Command line interface // ----------------------------------------------------------------------------- : Command line interface
...@@ -33,7 +34,7 @@ class CLISetInterface : public SetView { ...@@ -33,7 +34,7 @@ class CLISetInterface : public SetView {
void showUsage(); void showUsage();
void handleCommand(const String& command); void handleCommand(const String& command);
#if USE_SCRIPT_PROFILING #if USE_SCRIPT_PROFILING
void showProfilingStats(); void showProfilingStats(const FunctionProfile& parent, int level = 0);
#endif #endif
/// our own context, when no set is loaded /// our own context, when no set is loaded
......
...@@ -2791,6 +2791,12 @@ ...@@ -2791,6 +2791,12 @@
<File <File
RelativePath=".\script\parser.hpp"> RelativePath=".\script\parser.hpp">
</File> </File>
<File
RelativePath=".\script\profiler.cpp">
</File>
<File
RelativePath=".\script\profiler.hpp">
</File>
</Filter> </Filter>
<Filter <Filter
Name="use" Name="use"
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <script/context.hpp> #include <script/context.hpp>
#include <script/to_value.hpp> #include <script/to_value.hpp>
#include <script/profiler.hpp>
#include <util/error.hpp> #include <util/error.hpp>
#include <iostream> #include <iostream>
...@@ -18,95 +19,6 @@ Context::Context() ...@@ -18,95 +19,6 @@ Context::Context()
: level(0) : level(0)
{} {}
// ----------------------------------------------------------------------------- : Profiler
#if USE_SCRIPT_PROFILING
#ifndef UNICODE
#error "It looks like you are building the final release; disable USE_SCRIPT_PROFILING!"
#endif
#ifdef WIN32
typedef LONGLONG ProfileTime;
ProfileTime timer_now() {
LARGE_INTEGER i;
QueryPerformanceCounter(&i);
return i.QuadPart;
}
ProfileTime timer_resolution() {
LARGE_INTEGER i;
QueryPerformanceFrequency(&i);
return i.QuadPart;
}
#else
#error "Can't use profiler"
#endif
ProfileTime delta; ///< Time excluded
class Timer {
public:
Timer() {
start = timer_now() + delta;
}
ProfileTime time() {
ProfileTime end = timer_now() + delta;
ProfileTime diff = end - start;
start = end;
return diff;
}
void exclude_time() {
ProfileTime delta_delta = time();
delta -= delta_delta; // this time is not counted, even recursively
start -= delta_delta;
}
private:
ProfileTime start;
};
/// How much time was spent in each function?
struct FunctionProfile {
FunctionProfile() : time(0), calls(0) {}
ProfileTime time;
UInt calls;
};
VectorIntMap<unsigned int, FunctionProfile> variable_timings;
/// Profile a single function
struct Profiler {
public:
inline Profiler(Timer& timer, Variable function)
: timer(timer), function(function)
{
timer.exclude_time();
}
inline ~Profiler() {
ProfileTime time = timer.time();
if ((int)function < 0) return;
// per function timing
FunctionProfile& funprof = variable_timings[function];
funprof.time += time;
funprof.calls += 1;
timer.exclude_time();
}
private:
Timer& timer;
Variable function;
};
/// Get profile time in seconds and function names
void get_profile(vector<FunctionProfileItem>& out) {
double resolution = timer_resolution();
const vector<FunctionProfile>& times = variable_timings.get();
for (size_t i = 0 ; i < times.size() ; ++i) {
if (times[i].calls == 0) continue;
out.push_back(FunctionProfileItem(variable_to_string((Variable)i), times[i].time / resolution, times[i].calls));
}
}
#endif
// ----------------------------------------------------------------------------- : Evaluate // ----------------------------------------------------------------------------- : Evaluate
// Perform a unary simple instruction, store the result in a (not in *a) // Perform a unary simple instruction, store the result in a (not in *a)
......
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
class Dependency; class Dependency;
#define USE_SCRIPT_PROFILING 1 // TODO: Disable before release!
// ----------------------------------------------------------------------------- : VectorIntMap // ----------------------------------------------------------------------------- : VectorIntMap
/// A map like data structure that stores the elements in a vector. /// A map like data structure that stores the elements in a vector.
...@@ -127,9 +125,7 @@ class Context { ...@@ -127,9 +125,7 @@ class Context {
/// Get a variable name givin its value, returns (Variable)-1 if not found (slow!) /// Get a variable name givin its value, returns (Variable)-1 if not found (slow!)
Variable lookupVariableValue(const ScriptValueP& value); Variable lookupVariableValue(const ScriptValueP& value);
#if USE_SCRIPT_PROFILING friend class ScriptCompose;
friend class ScriptCompose;
#endif
}; };
/// A class that creates a local scope /// A class that creates a local scope
...@@ -142,20 +138,5 @@ class LocalScope { ...@@ -142,20 +138,5 @@ class LocalScope {
size_t scope; size_t scope;
}; };
// ----------------------------------------------------------------------------- : Profiler
#if USE_SCRIPT_PROFILING
struct FunctionProfileItem {
FunctionProfileItem() {}
FunctionProfileItem(const String& name, double time, int calls) : name(name), time(time), calls(calls) {}
inline bool operator < (const FunctionProfileItem& that) { return time < that.time; }
String name;
double time;
int calls;
};
void get_profile(vector<FunctionProfileItem>& out);
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <script/profiler.hpp>
#if USE_SCRIPT_PROFILING
// don't use script profiling in final build
#ifndef UNICODE
#error "It looks like you are building the final release; disable USE_SCRIPT_PROFILING!"
#endif
DECLARE_TYPEOF(map<size_t COMMA FunctionProfileP>);
// ----------------------------------------------------------------------------- : Timer
Timer::Timer() {
start = timer_now() + delta;
}
ProfileTime Timer::time() {
ProfileTime end = timer_now() + delta;
ProfileTime diff = end - start;
start = end;
return diff;
}
void Timer::exclude_time() {
ProfileTime delta_delta = time();
delta -= delta_delta;
start -= delta_delta;
}
ProfileTime Timer::delta = 0;
// ----------------------------------------------------------------------------- : FunctionProfile
FunctionProfile profile_root(_("root"));
inline bool compare_time(const FunctionProfileP& a, const FunctionProfileP& b) {
return a->time_ticks < b->time_ticks;
}
void FunctionProfile::get_children(vector<FunctionProfileP>& out) const {
FOR_EACH_CONST(c,children) {
out.push_back(c.second);
}
sort(out.begin(), out.end(), compare_time);
}
FunctionProfile profile_aggr(_("everywhere"));
void profile_aggregate(FunctionProfile& parent, int level, int max_level, const FunctionProfile& p);
void profile_aggregate(FunctionProfile& parent, int level, int max_level, size_t idx, const FunctionProfile& p) {
// add to item at idx
FunctionProfileP& fpp = parent.children[idx];
if (!fpp) {
fpp = new_intrusive1<FunctionProfile>(p.name);
}
fpp->time_ticks += p.time_ticks;
fpp->calls += p.calls;
// recurse
if (level == 0) {
profile_aggregate(parent, level, max_level, p);
}
if (level < max_level) {
profile_aggregate(*fpp, level + 1, max_level, p);
}
}
void profile_aggregate(FunctionProfile& parent, int level, int max_level, const FunctionProfile& p) {
FOR_EACH_CONST(c, p.children) {
profile_aggregate(parent, level, max_level, c.first, *c.second);
}
}
const FunctionProfile& profile_aggregated(int max_level) {
profile_aggr.children.clear();
profile_aggregate(profile_aggr, 0, max_level, profile_root);
return profile_aggr;
}
// ----------------------------------------------------------------------------- : Profiler
FunctionProfile* Profiler::function = &profile_root;
// Enter a function
Profiler::Profiler(Timer& timer, Variable function_name)
: timer(timer)
, parent(function) // push
{
if ((int)function_name >= 0) {
FunctionProfileP& fpp = parent->children[(size_t)function_name << 1 | 1];
if (!fpp) {
fpp = new_intrusive1<FunctionProfile>(variable_to_string(function_name));
}
function = fpp.get();
}
timer.exclude_time();
}
// Enter a function
Profiler::Profiler(Timer& timer, void* function_object, const String& function_name)
: timer(timer)
, parent(function) // push
{
FunctionProfileP& fpp = parent->children[(size_t)function_object];
if (!fpp) {
fpp = new_intrusive1<FunctionProfile>(function_name);
}
function = fpp.get();
timer.exclude_time();
}
// Leave a function
Profiler::~Profiler() {
ProfileTime time = timer.time();
if (function == parent) return; // don't count
function->time_ticks += time;
function->calls += 1;
function = parent; // pop
}
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_SCRIPT_PROFILER
#define HEADER_SCRIPT_PROFILER
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <script/script.hpp>
#include <script/context.hpp>
#define USE_SCRIPT_PROFILING 1
#if USE_SCRIPT_PROFILING
DECLARE_POINTER_TYPE(FunctionProfile);
// ----------------------------------------------------------------------------- : Timer
#ifdef WIN32
typedef LONGLONG ProfileTime;
inline ProfileTime timer_now() {
LARGE_INTEGER i;
QueryPerformanceCounter(&i);
return i.QuadPart;
}
inline ProfileTime timer_resolution() {
LARGE_INTEGER i;
QueryPerformanceFrequency(&i);
return i.QuadPart;
}
#else
#error "No Timer implementation, can't use profiler"
#endif
/// Simple execution timer
class Timer {
public:
Timer();
/// The time the timer has been running, resets the timer
inline ProfileTime time();
/// Exclude the time since the last reset from ALL running timers
inline void exclude_time();
private:
ProfileTime start;
static ProfileTime delta; ///< Time excluded
};
// ----------------------------------------------------------------------------- : FunctionProfile
/// How much time was spent in a function?
class FunctionProfile : public IntrusivePtrBase<FunctionProfile> {
public:
FunctionProfile(const String& name) : name(name), time_ticks(0), calls(0) {}
String name;
ProfileTime time_ticks;
UInt calls;
map<size_t,FunctionProfileP> children;
/// The children, sorted by time
void get_children(vector<FunctionProfileP>& out) const;
/// Time in seconds
inline double time() const { return time_ticks / (double)timer_resolution(); }
inline double avg_time() const { return time() / calls; }
};
/// The root profile
extern FunctionProfile profile_root;
const FunctionProfile& profile_aggregated(int level = 1);
// ----------------------------------------------------------------------------- : Profiler
/// Profile a single function call
class Profiler {
public:
Profiler(Timer& timer, Variable function_name);
Profiler(Timer& timer, void* function_object, const String& function_name);
~Profiler();
private:
Timer& timer;
static FunctionProfile* function; ///< function we are in
FunctionProfile* parent;
};
// ----------------------------------------------------------------------------- : EOF
#endif
#endif
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <script/script_manager.hpp> #include <script/script_manager.hpp>
#include <script/to_value.hpp> #include <script/to_value.hpp>
#include <script/functions/functions.hpp> #include <script/functions/functions.hpp>
#include <script/profiler.hpp>
#include <data/set.hpp> #include <data/set.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/game.hpp> #include <data/game.hpp>
...@@ -294,6 +295,10 @@ void SetScriptManager::updateAll() { ...@@ -294,6 +295,10 @@ void SetScriptManager::updateAll() {
Context& ctx = getContext(set.stylesheet); Context& ctx = getContext(set.stylesheet);
FOR_EACH(v, set.data) { FOR_EACH(v, set.data) {
try { try {
#if USE_SCRIPT_PROFILING
Timer t;
Profiler prof(t, v->fieldP.get(), _("update set.") + v->fieldP->name);
#endif
v->update(ctx); v->update(ctx);
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")), false, true); handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")), false, true);
...@@ -304,6 +309,10 @@ void SetScriptManager::updateAll() { ...@@ -304,6 +309,10 @@ void SetScriptManager::updateAll() {
Context& ctx = getContext(card); Context& ctx = getContext(card);
FOR_EACH(v, card->data) { FOR_EACH(v, card->data) {
try { try {
#if USE_SCRIPT_PROFILING
Timer t;
Profiler prof(t, v->fieldP.get(), _("update card.") + v->fieldP->name);
#endif
v->update(ctx); v->update(ctx);
} catch (const ScriptError& e) { } catch (const ScriptError& e) {
handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")), false, true); handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")), false, true);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment