388 lines
12 KiB
MQL5
388 lines
12 KiB
MQL5
/**********************************************************************************
|
|
* Copyright (C) 2020 Dominik Egert <info@freie-netze.de>
|
|
*
|
|
* This file is the lib_debug demonstration file.
|
|
*
|
|
* Lisence applied: Free, no license applied to this file.
|
|
*
|
|
* Author Dominik Egert / Freie Netze UG.
|
|
**********************************************************************************
|
|
*
|
|
* Version: 1.01
|
|
* State: public
|
|
*
|
|
* File information
|
|
* ================
|
|
*
|
|
*/
|
|
#property version "1.01"
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Performance profiling
|
|
//
|
|
// This feature is only active in runtime builds of your program, and if you activate the flag
|
|
// as shown below.
|
|
//
|
|
// Compile your project and load it onto a chart in the Terminal.
|
|
//
|
|
|
|
|
|
|
|
/////////////////////////////////////////
|
|
//
|
|
// Include the library
|
|
//
|
|
#define LIB_PERF_PROFILING
|
|
#include <MQLplus/lib_debug.mqh>
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Some initial debug message
|
|
DBG_MSG("Example how to use lib_debug.mqh for performance metrics");
|
|
|
|
// Output details about current program
|
|
// Available in debugging and runtime
|
|
printf("%s", DBG_STR_EX45_FILEINFO);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Standalone performance macros example
|
|
//
|
|
// To measure certain parts of code in a standalone environment,
|
|
// following macros are implemented to do this.
|
|
//
|
|
// There are three variations of this macro available. Depending on
|
|
// the return value of the function to be measured, either having
|
|
// a return value, a return object or not having a return value, you
|
|
// need to use the appropiate macro.
|
|
//
|
|
// PERF_COUNTER_TIMEIT_V (V = void)
|
|
// This macro is used to measure functions or function sequences
|
|
// that do not have a return value.
|
|
//
|
|
// PERF_COUNTER_TIMEIT_R (R = return)
|
|
// This macro will return the resulting value (return value)
|
|
// of the function being called, or their combination of such.
|
|
// For a better understanding, see the examples below.
|
|
//
|
|
// PERF_COUNTER_TIMEIT_O (O = Object)
|
|
// This macro will return the object (returned by the function)
|
|
// of the function being called.
|
|
//
|
|
// NOTE:
|
|
//
|
|
// PERF_COUNTER_TIMEIT_V may not be mentioned more than once per line of code.
|
|
// Internally the macro uses the __LINE__ macro for identifying the call,
|
|
// therefore it is mandatory to have only one call per code line.
|
|
// Also be aware, if including this macro inside of another macro, all
|
|
// statements will be on one line of code.
|
|
//
|
|
// PERF_COUNTER_TIMEIT_R uses the complier macro __COUNTER__ to identify
|
|
// unique its callings. Be aware, in case you rely on the sequence of
|
|
// counting provided by __COUNTER__ within your code. Each mentioning of
|
|
// the macro "PERF_COUNTER_TIMEIT_R" will increase the __COUNTER__ by one.
|
|
//
|
|
// PERF_COUNTER_TIMEIT_O notes are same as for PERF_COUNTER_TIMEIT_R, with
|
|
// one addition: The Copy-Constructor of the returned object will be called
|
|
// twice. The first call contributes to the measurement results, the second
|
|
// consequently does not. In order to encapusalte the function calls, an
|
|
// additional copy of the object is required within the process of returning
|
|
// values from the function.
|
|
//
|
|
// For pointers as return value, either macro "PERF_COUNTER_TIMEIT_R" or
|
|
// "PERF_COUNTER_TIMEIT_O" can be used. - Both work the same way in this case.
|
|
//
|
|
|
|
// A single function call
|
|
PERF_COUNTER_TIMEIT_V(
|
|
printf("Some text A");
|
|
);
|
|
|
|
// A block sequence of function calls
|
|
PERF_COUNTER_TIMEIT_V(
|
|
printf("Some text B");
|
|
printf("Some text C");
|
|
);
|
|
|
|
// Activate timer
|
|
EventSetTimer(10);
|
|
|
|
// Example of complex measurements
|
|
b_start();
|
|
b_start();
|
|
b_start();
|
|
|
|
// Return
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
|
|
// Some simple functions to be measured in their runtime
|
|
int f1() { Sleep(100); return(1); }
|
|
int f2() { Sleep(200); return(2); }
|
|
int f3() { Sleep(300); return(3); }
|
|
|
|
// A collection of function calls
|
|
int g()
|
|
{
|
|
return(PERF_COUNTER_TIMEIT_R(f1() + f2() + f3() == 6) ?
|
|
PERF_COUNTER_TIMEIT_R(f1() * f2()) : 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Service program start function |
|
|
//+------------------------------------------------------------------+
|
|
void b_start()
|
|
{
|
|
if (PERF_COUNTER_TIMEIT_R(g()) == 2)
|
|
{
|
|
PERF_COUNTER_TIMEIT_V(
|
|
Print(f3() == 3);
|
|
);
|
|
}
|
|
|
|
PERF_COUNTER_TIMEIT_V(
|
|
Print(f1());
|
|
);
|
|
|
|
PERF_COUNTER_TIMEIT_V(
|
|
Print(f2());
|
|
);
|
|
|
|
return;
|
|
};
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Destroy timer
|
|
EventKillTimer();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// In OnTick we will make use of the full trace integration macros.
|
|
//
|
|
// Here the macros will be reduced to only perform runtime
|
|
// performance data gathering and printing to expert journal.
|
|
//
|
|
|
|
// Turn on tracing
|
|
#define DBG_TRACE_EXPERT_MAIN_ONTICK
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
/////////////////////////////////////
|
|
// Function debug trace code
|
|
#ifdef DBG_TRACE_EXPERT_MAIN_ONTICK
|
|
#undef DBG_TRACE_EXPERT_MAIN_ONTICK
|
|
#define DBG_TRACE_EXPERT_MAIN_ONTICK(x) x
|
|
#define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN DBG_MSG_TRACE_RETURN
|
|
|
|
#else
|
|
#define DBG_TRACE_EXPERT_MAIN_ONTICK(x)
|
|
#define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN DBG_MSG_NOTRACE_RETURN
|
|
|
|
#endif
|
|
/////////////////////////////////////
|
|
void OnTick()
|
|
{
|
|
DBG_TRACE_EXPERT_MAIN_ONTICK(
|
|
DBG_MSG_TRACE_BEGIN;
|
|
);
|
|
PERF_COUNTER_BEGIN;
|
|
|
|
// Local init
|
|
int some_var = MathRand();
|
|
|
|
|
|
// Some eventual exit at annother point inside the functions body
|
|
if((some_var % 2048) == 0)
|
|
{ DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; }
|
|
|
|
|
|
// Return
|
|
DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// In OnTimer we will use only the performance metrics macros.
|
|
//
|
|
// For ease of use, a return macro will be defined. This is the
|
|
// only requirement for performance metrics to be displayed in the
|
|
// experts journal.
|
|
//
|
|
// Performance macros are enabled only in release versions
|
|
// of the code, they are disabled inside of debugging environments.
|
|
//
|
|
// Debugging environment is defined by the flag LIB_DEBUG.
|
|
// To enable performance macros, define LIB_PERF_PROFILING.
|
|
//
|
|
// NOTE:
|
|
// The macro "DBG_MSG_TRACE_RETURN" or "DBG_MSG_NOTRACE_RETURN"
|
|
// must be used, so the performance control block will be properly closed.
|
|
// Therefore we define relevant macros again, as shown in other examples.
|
|
//
|
|
// Enable/disable tracing does not influence the perfomrance macros, as tracing
|
|
// is only enabled when "LIB_DEBUG" is defined and "PERF_*"-macros are disabled.
|
|
// You will get performance measurements when the global macro "LIB_PERF_PROFILING"
|
|
// is defined and the global macro "LIB_DEBUG" is not defined.
|
|
//
|
|
//
|
|
// We can now define additional, custom performance blocks on a global level.
|
|
// These work accross functions and can be used to trace certain parts of the
|
|
// code. - Each block has its own call counter, therefore it is not advised
|
|
// to reuse the blocks for different code sections, except of course thats
|
|
// exactly what you want to measure.
|
|
//
|
|
// These blocks may overlap, if required. - It is necessary t ounderstand,
|
|
// when doing so, you will also measure the times each macro takes. The execution
|
|
// of these macros would be part of your measurement.
|
|
//
|
|
// See example below.
|
|
//
|
|
|
|
// Create additional performance counters
|
|
// IDs can be specified freely
|
|
PERF_COUNTER_DEFINE_ID(on_timer_for_loop);
|
|
|
|
// Additional example (these are not used)
|
|
PERF_COUNTER_DEFINE_ID(if_g_func);
|
|
PERF_COUNTER_DEFINE_ID(call_f3_func);
|
|
PERF_COUNTER_DEFINE_ID(printf_f1);
|
|
PERF_COUNTER_DEFINE_ID(printf_f2);
|
|
|
|
// Performance block IDs can be created on a global scope as well
|
|
// as on function local scope and even in code blocks inside of
|
|
// functions scope.
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Timer function |
|
|
//+------------------------------------------------------------------+
|
|
/////////////////////////////////////
|
|
// Function performance trace code
|
|
#define DBG_TRACE_EXPERT_MAIN_ONTIMER_RETURN DBG_MSG_NOTRACE_RETURN
|
|
|
|
/////////////////////////////////////
|
|
void OnTimer()
|
|
{
|
|
PERF_COUNTER_BEGIN;
|
|
|
|
// Disable timer for demo purpose
|
|
EventKillTimer();
|
|
|
|
// Measuring a loop
|
|
double sum = NULL;
|
|
|
|
PERF_COUNTER_BEGIN_ID(on_timer_for_loop);
|
|
for(int cnt = NULL; (cnt < 1000000) && !_StopFlag; cnt++)
|
|
{
|
|
sum += 1.0;
|
|
}
|
|
PERF_COUNTER_END_ID(on_timer_for_loop);
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Here an example of possible nested performance blocks.
|
|
// This example is very theoretical, but shows how blocks can be applied.
|
|
//
|
|
|
|
// Local scope
|
|
{
|
|
// These definitions can be on any scope in
|
|
// the hirarchy above this scope of the function all
|
|
// the way up to the global scope.
|
|
PERF_COUNTER_DEFINE_ID(a_local_scope);
|
|
PERF_COUNTER_DEFINE_ID(nested_level_1);
|
|
PERF_COUNTER_DEFINE_ID(overlapping_block_a);
|
|
PERF_COUNTER_DEFINE_ID(overlapping_block_b);
|
|
|
|
|
|
// Begin measurement of most outer block
|
|
PERF_COUNTER_BEGIN_ID(a_local_scope);
|
|
|
|
// Have some code here (not mandatory)
|
|
|
|
|
|
// Nested performance block 1
|
|
PERF_COUNTER_BEGIN_ID(nested_level_1);
|
|
|
|
// Have more code here
|
|
|
|
|
|
// Overlapping execution block
|
|
PERF_COUNTER_BEGIN_ID(overlapping_block_a);
|
|
|
|
// Maybe some code here for block A
|
|
|
|
|
|
// Open next block
|
|
PERF_COUNTER_BEGIN_ID(overlapping_block_b);
|
|
|
|
// Have some code here for block A and B (shared code block)
|
|
|
|
|
|
// Now closing block A
|
|
PERF_COUNTER_END_ID(overlapping_block_a);
|
|
|
|
// Some code for block B
|
|
|
|
|
|
// Now closing block B
|
|
PERF_COUNTER_END_ID(overlapping_block_b);
|
|
|
|
// Maybe more code here for nested level 1
|
|
|
|
|
|
// Close nested block 1
|
|
PERF_COUNTER_END_ID(nested_level_1);
|
|
|
|
// Optionally have code here as well....
|
|
|
|
|
|
// Close most outer block
|
|
PERF_COUNTER_END_ID(a_local_scope);
|
|
}
|
|
|
|
|
|
// The above example will also measure the performance counters itself.
|
|
// Although they are held as short as possible, code-wise, it will
|
|
// have some influence on the pure code thats being measured.
|
|
//
|
|
// The impact can vary, depending on the memory-paging that happens
|
|
// by accessing different areas of memory. - The variations will be
|
|
// within the margins of error and therefore can (mostly) be ignored.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Return
|
|
DBG_TRACE_EXPERT_MAIN_ONTIMER_RETURN;
|
|
}
|