nightingale1/vendor/github.com/open-falcon/rrdlite/rrd_hw_update.c

476 lines
19 KiB
C

/*****************************************************************************
* rrd_hw_update.c Functions for updating a Holt-Winters RRA
****************************************************************************/
#include "rrd_tool.h"
#include "rrd_format.h"
#include "rrd_hw_math.h"
#include "rrd_hw_update.h"
static void init_slope_intercept(
unival *coefs,
unsigned short CDP_scratch_idx)
{
#ifdef DEBUG
fprintf(stderr, "Initialization of slope/intercept\n");
#endif
coefs[CDP_hw_intercept].u_val = coefs[CDP_scratch_idx].u_val;
coefs[CDP_hw_last_intercept].u_val = coefs[CDP_scratch_idx].u_val;
/* initialize the slope to 0 */
coefs[CDP_hw_slope].u_val = 0.0;
coefs[CDP_hw_last_slope].u_val = 0.0;
/* initialize null count to 1 */
coefs[CDP_null_count].u_cnt = 1;
coefs[CDP_last_null_count].u_cnt = 1;
}
static int hw_is_violation(
rrd_value_t observed,
rrd_value_t prediction,
rrd_value_t deviation,
rrd_value_t delta_pos,
rrd_value_t delta_neg)
{
return (observed > prediction + delta_pos * deviation
|| observed < prediction - delta_neg * deviation);
}
int update_hwpredict(
rrd_t *rrd,
unsigned long cdp_idx,
unsigned long rra_idx,
unsigned long ds_idx,
unsigned short CDP_scratch_idx,
hw_functions_t * functions)
{
rrd_value_t prediction;
unsigned long dependent_rra_idx, seasonal_cdp_idx;
unival *coefs = rrd->cdp_prep[cdp_idx].scratch;
rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
rrd_value_t seasonal_coef;
/* save coefficients from current prediction */
coefs[CDP_hw_last_intercept].u_val = coefs[CDP_hw_intercept].u_val;
coefs[CDP_hw_last_slope].u_val = coefs[CDP_hw_slope].u_val;
coefs[CDP_last_null_count].u_cnt = coefs[CDP_null_count].u_cnt;
/* retrieve the current seasonal coef */
dependent_rra_idx = current_rra->par[RRA_dependent_rra_idx].u_cnt;
seasonal_cdp_idx = dependent_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
seasonal_coef = (dependent_rra_idx < rra_idx)
? rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val
: rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
/* compute the prediction */
if (isnan(coefs[CDP_hw_intercept].u_val)
|| isnan(coefs[CDP_hw_slope].u_val)
|| isnan(seasonal_coef)) {
prediction = DNAN;
/* bootstrap initialization of slope and intercept */
if (isnan(coefs[CDP_hw_intercept].u_val) &&
!isnan(coefs[CDP_scratch_idx].u_val)) {
init_slope_intercept(coefs, CDP_scratch_idx);
}
/* if seasonal coefficient is NA, then don't update intercept, slope */
} else {
prediction = functions->predict(coefs[CDP_hw_intercept].u_val,
coefs[CDP_hw_slope].u_val,
coefs[CDP_null_count].u_cnt,
seasonal_coef);
#ifdef DEBUG
fprintf(stderr,
"computed prediction: %f (intercept %f, slope %f, season %f)\n",
prediction, coefs[CDP_hw_intercept].u_val,
coefs[CDP_hw_slope].u_val, seasonal_coef);
#endif
if (isnan(coefs[CDP_scratch_idx].u_val)) {
/* NA value, no updates of intercept, slope;
* increment the null count */
(coefs[CDP_null_count].u_cnt)++;
} else {
/* update the intercept */
coefs[CDP_hw_intercept].u_val =
functions->intercept(current_rra->par[RRA_hw_alpha].u_val,
coefs[CDP_scratch_idx].u_val,
seasonal_coef, coefs);
/* update the slope */
coefs[CDP_hw_slope].u_val =
functions->slope(current_rra->par[RRA_hw_beta].u_val, coefs);
/* reset the null count */
coefs[CDP_null_count].u_cnt = 1;
#ifdef DEBUG
fprintf(stderr, "Updating intercept = %f, slope = %f\n",
coefs[CDP_hw_intercept].u_val, coefs[CDP_hw_slope].u_val);
#endif
}
}
/* store the prediction for writing */
coefs[CDP_scratch_idx].u_val = prediction;
return 0;
}
int update_seasonal(
rrd_t *rrd,
unsigned long cdp_idx,
unsigned long rra_idx,
unsigned long ds_idx,
unsigned short CDP_scratch_idx,
rrd_value_t *seasonal_coef,
hw_functions_t * functions)
{
/* TODO: extract common if subblocks in the wake of I/O optimization */
rrd_value_t intercept, seasonal;
rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
rra_def_t *hw_rra =
&(rrd->rra_def[current_rra->par[RRA_dependent_rra_idx].u_cnt]);
/* obtain cdp_prep index for HWPREDICT */
unsigned long hw_cdp_idx = (current_rra->par[RRA_dependent_rra_idx].u_cnt)
* (rrd->stat_head->ds_cnt) + ds_idx;
unival *coefs = rrd->cdp_prep[hw_cdp_idx].scratch;
/* update seasonal coefficient in cdp prep areas */
seasonal = rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val;
rrd->cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = seasonal;
rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val =
seasonal_coef[ds_idx];
if (isnan(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)) {
/* no update, store the old value unchanged,
* doesn't matter if it is NA */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = seasonal;
return 0;
}
/* update seasonal value for disk */
if (current_rra->par[RRA_dependent_rra_idx].u_cnt < rra_idx) {
/* associated HWPREDICT has already been updated */
/* check for possible NA values */
if (isnan(coefs[CDP_hw_last_intercept].u_val)
|| isnan(coefs[CDP_hw_last_slope].u_val)) {
/* this should never happen, as HWPREDICT was already updated */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
} else if (isnan(seasonal)) {
/* initialization: intercept is not currently being updated */
#ifdef DEBUG
fprintf(stderr, "Initialization of seasonal coef %lu\n",
rrd->rra_ptr[rra_idx].cur_row);
#endif
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->init_seasonality(rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].u_val,
coefs[CDP_hw_last_intercept].
u_val);
} else {
intercept = coefs[CDP_hw_intercept].u_val;
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->seasonality(current_rra->par[RRA_seasonal_gamma].
u_val,
rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].u_val,
intercept, seasonal);
#ifdef DEBUG
fprintf(stderr,
"Updating seasonal = %f (params: gamma %f, new intercept %f, old seasonal %f)\n",
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,
current_rra->par[RRA_seasonal_gamma].u_val,
intercept, seasonal);
#endif
}
} else {
/* SEASONAL array is updated first, which means the new intercept
* hasn't be computed; so we compute it here. */
/* check for possible NA values */
if (isnan(coefs[CDP_hw_intercept].u_val)
|| isnan(coefs[CDP_hw_slope].u_val)) {
/* Initialization of slope and intercept will occur.
* force seasonal coefficient to 0 or 1. */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->identity;
} else if (isnan(seasonal)) {
/* initialization: intercept will not be updated
* CDP_hw_intercept = CDP_hw_last_intercept; just need to
* subtract/divide by this baseline value. */
#ifdef DEBUG
fprintf(stderr, "Initialization of seasonal coef %lu\n",
rrd->rra_ptr[rra_idx].cur_row);
#endif
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->init_seasonality(rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].u_val,
coefs[CDP_hw_intercept].u_val);
} else {
/* Note that we must get CDP_scratch_idx from SEASONAL array, as CDP_scratch_idx
* for HWPREDICT array will be DNAN. */
intercept = functions->intercept(hw_rra->par[RRA_hw_alpha].u_val,
rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].u_val,
seasonal, coefs);
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->seasonality(current_rra->par[RRA_seasonal_gamma].
u_val,
rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].u_val,
intercept, seasonal);
}
}
#ifdef DEBUG
fprintf(stderr, "seasonal coefficient set= %f\n",
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
#endif
return 0;
}
int update_devpredict(
rrd_t *rrd,
unsigned long cdp_idx,
unsigned long rra_idx,
unsigned long ds_idx,
unsigned short CDP_scratch_idx)
{
/* there really isn't any "update" here; the only reason this information
* is stored separately from DEVSEASONAL is to preserve deviation predictions
* for a longer duration than one seasonal cycle. */
unsigned long seasonal_cdp_idx =
(rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt)
* (rrd->stat_head->ds_cnt) + ds_idx;
if (rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx) {
/* associated DEVSEASONAL array already updated */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
=
rrd->cdp_prep[seasonal_cdp_idx].
scratch[CDP_last_seasonal_deviation].u_val;
} else {
/* associated DEVSEASONAL not yet updated */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
=
rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_seasonal_deviation].
u_val;
}
return 0;
}
int update_devseasonal(
rrd_t *rrd,
unsigned long cdp_idx,
unsigned long rra_idx,
unsigned long ds_idx,
unsigned short CDP_scratch_idx,
rrd_value_t *seasonal_dev,
hw_functions_t * functions)
{
rrd_value_t prediction = 0, seasonal_coef = DNAN;
rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
/* obtain cdp_prep index for HWPREDICT */
unsigned long hw_rra_idx = current_rra->par[RRA_dependent_rra_idx].u_cnt;
unsigned long hw_cdp_idx = hw_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
unsigned long seasonal_cdp_idx;
unival *coefs = rrd->cdp_prep[hw_cdp_idx].scratch;
rrd->cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val =
rrd->cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val;
/* retrieve the next seasonal deviation value, could be NA */
rrd->cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val =
seasonal_dev[ds_idx];
/* retrieve the current seasonal_coef (not to be confused with the
* current seasonal deviation). Could make this more readable by introducing
* some wrapper functions. */
seasonal_cdp_idx =
(rrd->rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt)
* (rrd->stat_head->ds_cnt) + ds_idx;
if (rrd->rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx)
/* SEASONAL array already updated */
seasonal_coef =
rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].
u_val;
else
/* SEASONAL array not yet updated */
seasonal_coef =
rrd->cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
/* compute the abs value of the difference between the prediction and
* observed value */
if (hw_rra_idx < rra_idx) {
/* associated HWPREDICT has already been updated */
if (isnan(coefs[CDP_hw_last_intercept].u_val) ||
isnan(coefs[CDP_hw_last_slope].u_val) || isnan(seasonal_coef)) {
/* one of the prediction values is uinitialized */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
return 0;
} else {
prediction =
functions->predict(coefs[CDP_hw_last_intercept].u_val,
coefs[CDP_hw_last_slope].u_val,
coefs[CDP_last_null_count].u_cnt,
seasonal_coef);
}
} else {
/* associated HWPREDICT has NOT been updated */
if (isnan(coefs[CDP_hw_intercept].u_val) ||
isnan(coefs[CDP_hw_slope].u_val) || isnan(seasonal_coef)) {
/* one of the prediction values is uinitialized */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
return 0;
} else {
prediction = functions->predict(coefs[CDP_hw_intercept].u_val,
coefs[CDP_hw_slope].u_val,
coefs[CDP_null_count].u_cnt,
seasonal_coef);
}
}
if (isnan(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)) {
/* no update, store existing value unchanged, doesn't
* matter if it is NA */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
rrd->cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
} else
if (isnan
(rrd->cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].
u_val)) {
/* initialization */
#ifdef DEBUG
fprintf(stderr, "Initialization of seasonal deviation\n");
#endif
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->init_seasonal_deviation(prediction,
rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].
u_val);
} else {
/* exponential smoothing update */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
functions->seasonal_deviation(rrd->rra_def[rra_idx].
par[RRA_seasonal_gamma].u_val,
prediction,
rrd->cdp_prep[cdp_idx].
scratch[CDP_scratch_idx].u_val,
rrd->cdp_prep[cdp_idx].
scratch
[CDP_last_seasonal_deviation].
u_val);
}
return 0;
}
/* Check for a failure based on a threshold # of violations within the specified
* window. */
int update_failures(
rrd_t *rrd,
unsigned long cdp_idx,
unsigned long rra_idx,
unsigned long ds_idx,
unsigned short CDP_scratch_idx,
hw_functions_t * functions)
{
/* detection of a violation depends on 3 RRAs:
* HWPREDICT, SEASONAL, and DEVSEASONAL */
rra_def_t *current_rra = &(rrd->rra_def[rra_idx]);
unsigned long dev_rra_idx = current_rra->par[RRA_dependent_rra_idx].u_cnt;
rra_def_t *dev_rra = &(rrd->rra_def[dev_rra_idx]);
unsigned long hw_rra_idx = dev_rra->par[RRA_dependent_rra_idx].u_cnt;
rra_def_t *hw_rra = &(rrd->rra_def[hw_rra_idx]);
unsigned long seasonal_rra_idx = hw_rra->par[RRA_dependent_rra_idx].u_cnt;
unsigned long temp_cdp_idx;
rrd_value_t deviation = DNAN;
rrd_value_t seasonal_coef = DNAN;
rrd_value_t prediction = DNAN;
char violation = 0;
unsigned short violation_cnt = 0, i;
char *violations_array;
/* usual checks to determine the order of the RRAs */
temp_cdp_idx = dev_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
if (rra_idx < seasonal_rra_idx) {
/* DEVSEASONAL not yet updated */
deviation =
rrd->cdp_prep[temp_cdp_idx].scratch[CDP_seasonal_deviation].u_val;
} else {
/* DEVSEASONAL already updated */
deviation =
rrd->cdp_prep[temp_cdp_idx].scratch[CDP_last_seasonal_deviation].
u_val;
}
if (!isnan(deviation)) {
temp_cdp_idx = seasonal_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
if (rra_idx < seasonal_rra_idx) {
/* SEASONAL not yet updated */
seasonal_coef =
rrd->cdp_prep[temp_cdp_idx].scratch[CDP_hw_seasonal].u_val;
} else {
/* SEASONAL already updated */
seasonal_coef =
rrd->cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_seasonal].
u_val;
}
/* in this code block, we know seasonal coef is not DNAN, because deviation is not
* null */
temp_cdp_idx = hw_rra_idx * (rrd->stat_head->ds_cnt) + ds_idx;
if (rra_idx < hw_rra_idx) {
/* HWPREDICT not yet updated */
prediction =
functions->predict(rrd->cdp_prep[temp_cdp_idx].
scratch[CDP_hw_intercept].u_val,
rrd->cdp_prep[temp_cdp_idx].
scratch[CDP_hw_slope].u_val,
rrd->cdp_prep[temp_cdp_idx].
scratch[CDP_null_count].u_cnt,
seasonal_coef);
} else {
/* HWPREDICT already updated */
prediction =
functions->predict(rrd->cdp_prep[temp_cdp_idx].
scratch[CDP_hw_last_intercept].u_val,
rrd->cdp_prep[temp_cdp_idx].
scratch[CDP_hw_last_slope].u_val,
rrd->cdp_prep[temp_cdp_idx].
scratch[CDP_last_null_count].u_cnt,
seasonal_coef);
}
/* determine if the observed value is a violation */
if (!isnan(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)) {
if (hw_is_violation
(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,
prediction, deviation, current_rra->par[RRA_delta_pos].u_val,
current_rra->par[RRA_delta_neg].u_val)) {
violation = 1;
}
} else {
violation = 1; /* count DNAN values as violations */
}
}
/* determine if a failure has occurred and update the failure array */
violation_cnt = violation;
violations_array = (char *) ((void *) rrd->cdp_prep[cdp_idx].scratch);
for (i = current_rra->par[RRA_window_len].u_cnt; i > 1; i--) {
/* shift */
violations_array[i - 1] = violations_array[i - 2];
violation_cnt += violations_array[i - 1];
}
violations_array[0] = violation;
if (violation_cnt < current_rra->par[RRA_failure_threshold].u_cnt)
/* not a failure */
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 0.0;
else
rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 1.0;
return (rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
}