/********************************************************************************** * Copyright (C) 2020 Dominik Egert * * 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 //+------------------------------------------------------------------+ //| 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; }