PG-222: Add benchmark support for measuring hook execution time.

Added a new view 'pg_stat_monitor_hook_stats' that provide execution
time statistics for all hooks installed by the module, following is a
description of the fields:
  -       hook: The hook function name.
  -   min_time: The fastest execution time recorded for the given hook.
  -   max_time: The slowest execution time recorded for the given hook.
  - total_time: Total execution time taken by all calls to the hook.
  -   avg_time: Average execution time of a call to the hook.
  -     ncalls: Total number of calls to the hook.
  - load_comparison: A percentual of time taken by an individual hook
                     compared to every other hook.

To enable benchmark, code must be compiled with -DBENCHMARK flag, this
will make the hook functions to be replaced by a function with the same
name plus a '_benchmark' suffix, e.g. hook_function_benchmark.

The hook_function_benchmark will call the original function and
calculate the amount of time it took to execute, than it will update
statistics for that hook.
This commit is contained in:
Diego Fronza
2021-08-19 15:45:06 -04:00
parent 641c91980c
commit 775c087fb2
4 changed files with 381 additions and 23 deletions

View File

@@ -413,4 +413,76 @@ void pgss_startup(void);
#define PGSM_QUERY_PLAN get_conf(12)->guc_variable
#define PGSM_TRACK_PLANNING get_conf(13)->guc_variable
/*---- Benchmarking ----*/
#ifdef BENCHMARK
/*
* These enumerator values are used as index in the hook stats array.
* STATS_START and STATS_END are used only to delimit the range.
* STATS_END is also the length of the valid items in the enum.
*/
enum pg_hook_stats_id {
STATS_START = -1,
STATS_PGSS_POST_PARSE_ANALYZE,
STATS_PGSS_EXECUTORSTART,
STATS_PGSS_EXECUTORUN,
STATS_PGSS_EXECUTORFINISH,
STATS_PGSS_EXECUTOREND,
STATS_PGSS_PROCESSUTILITY,
#if PG_VERSION_NUM >= 130000
STATS_PGSS_PLANNER_HOOK,
#endif
STATS_PGSM_EMIT_LOG_HOOK,
STATS_PGSS_EXECUTORCHECKPERMS,
STATS_END
};
/* Hold time to execute statistics for a hook. */
struct pg_hook_stats_t {
char hook_name[64];
double min_time;
double max_time;
double total_time;
uint64 ncalls;
};
#define HOOK_STATS_SIZE MAXALIGN((size_t)STATS_END * sizeof(struct pg_hook_stats_t))
/* Allocate a pg_hook_stats_t array of size HOOK_STATS_SIZE on shared memory. */
void init_hook_stats(void);
/* Update hook time execution statistics. */
void update_hook_stats(enum pg_hook_stats_id hook_id, double time_elapsed);
/*
* Macro used to declare a hook function:
* Example:
* DECLARE_HOOK(void my_hook, const char *query, size_t length);
* Will expand to:
* static void my_hook(const char *query, size_t length);
* static void my_hook_benchmark(const char *query, size_t length);
*/
#define DECLARE_HOOK(hook, ...) \
static hook(__VA_ARGS__); \
static hook##_benchmark(__VA_ARGS__);
/*
* Macro used to wrap a hook when pg_stat_monitor is compiled with -DBENCHMARK.
*
* It is intended to be used as follows in _PG_init():
* pg_hook_function = HOOK(my_hook_function);
* Then, if pg_stat_monitor is compiled with -DBENCHMARK this will expand to:
* pg_hook_name = my_hook_function_benchmark;
* Otherwise it will simple expand to:
* pg_hook_name = my_hook_function;
*/
#define HOOK(name) name##_benchmark
#else /* #ifdef BENCHMARK */
#define DECLARE_HOOK(hook, ...) \
static hook(__VA_ARGS__);
#define HOOK(name) name
#define HOOK_STATS_SIZE 0
#endif
#endif