You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
8.0 KiB
374 lines
8.0 KiB
/******************************************************************************
|
|
*
|
|
* Copyright © International Business Machines Corp., 2006, 2008
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
* the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* NAME
|
|
* libstats.c
|
|
*
|
|
* DESCRIPTION
|
|
* Some basic statistical analysis convenience tools.
|
|
*
|
|
*
|
|
* USAGE:
|
|
* To be included in test cases
|
|
*
|
|
* AUTHOR
|
|
* Darren Hart <dvhltc@us.ibm.com>
|
|
*
|
|
* HISTORY
|
|
* 2006-Oct-17: Initial version by Darren Hart
|
|
* 2009-Jul-22: Addition of stats_container_append function by Kiran Prakash
|
|
*
|
|
* TODO: the save routine for gnuplot plotting should be more modular...
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <libstats.h>
|
|
#include <librttest.h>
|
|
|
|
#include "../include/realtime_config.h"
|
|
|
|
#ifndef HAVE_EXP10
|
|
# define exp10(x) (exp((x) * log(10)))
|
|
#endif
|
|
|
|
int save_stats = 0;
|
|
|
|
/* static helper functions */
|
|
static int stats_record_compare(const void *a, const void *b)
|
|
{
|
|
int ret = 0;
|
|
stats_record_t *rec_a = (stats_record_t *) a;
|
|
stats_record_t *rec_b = (stats_record_t *) b;
|
|
if (rec_a->y < rec_b->y)
|
|
ret = -1;
|
|
else if (rec_a->y > rec_b->y)
|
|
ret = 1;
|
|
return ret;
|
|
}
|
|
|
|
/* function implementations */
|
|
int stats_container_init(stats_container_t * data, long size)
|
|
{
|
|
data->size = size;
|
|
data->index = -1;
|
|
data->records = calloc(size, sizeof(stats_record_t));
|
|
if (!data->records)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int stats_container_append(stats_container_t * data, stats_record_t rec)
|
|
{
|
|
int myindex = ++data->index;
|
|
if (myindex >= data->size) {
|
|
debug(DBG_ERR, "Number of elements cannot be more than %ld\n",
|
|
data->size);
|
|
data->index--;
|
|
return -1;
|
|
}
|
|
data->records[myindex] = rec;
|
|
return myindex;
|
|
}
|
|
|
|
int stats_container_resize(stats_container_t * data, long size)
|
|
{
|
|
stats_record_t *newrecords =
|
|
realloc(data->records, size * sizeof(stats_record_t));
|
|
if (!newrecords)
|
|
return -1;
|
|
data->records = newrecords;
|
|
if (data->size < size)
|
|
memset(data->records + data->size, 0, size - data->size);
|
|
data->size = size;
|
|
return 0;
|
|
}
|
|
|
|
int stats_container_free(stats_container_t * data)
|
|
{
|
|
free(data->records);
|
|
return 0;
|
|
}
|
|
|
|
int stats_sort(stats_container_t * data, enum stats_sort_method method)
|
|
{
|
|
/* method not implemented, always ascending on y atm */
|
|
qsort(data->records, data->index + 1, sizeof(stats_record_t),
|
|
stats_record_compare);
|
|
return 0;
|
|
}
|
|
|
|
float stats_stddev(stats_container_t * data)
|
|
{
|
|
long i;
|
|
float sd, avg, sum, delta;
|
|
long n;
|
|
|
|
sd = 0.0;
|
|
n = data->index + 1;
|
|
sum = 0.0;
|
|
|
|
/* calculate the mean */
|
|
for (i = 0; i < n; i++) {
|
|
sum += data->records[i].y;
|
|
}
|
|
avg = sum / (float)n;
|
|
|
|
/* calculate the standard deviation */
|
|
sum = 0.0;
|
|
for (i = 0; i < n; i++) {
|
|
delta = (data->records[i].y - avg);
|
|
sum += delta * delta;
|
|
}
|
|
sum /= n;
|
|
|
|
sd = sqrt(sum);
|
|
|
|
return sd;
|
|
}
|
|
|
|
float stats_avg(stats_container_t * data)
|
|
{
|
|
long i;
|
|
float avg, sum;
|
|
long n;
|
|
|
|
n = data->index + 1;
|
|
sum = 0.0;
|
|
|
|
/* calculate the mean */
|
|
for (i = 0; i < n; i++) {
|
|
sum += data->records[i].y;
|
|
}
|
|
avg = sum / (float)n;
|
|
|
|
return avg;
|
|
}
|
|
|
|
long stats_min(stats_container_t * data)
|
|
{
|
|
long i;
|
|
long min;
|
|
long n;
|
|
|
|
n = data->index + 1;
|
|
|
|
/* calculate the mean */
|
|
min = data->records[0].y;
|
|
for (i = 1; i < n; i++) {
|
|
if (data->records[i].y < min) {
|
|
min = data->records[i].y;
|
|
}
|
|
}
|
|
|
|
return min;
|
|
}
|
|
|
|
long stats_max(stats_container_t * data)
|
|
{
|
|
long i;
|
|
long max;
|
|
long n;
|
|
|
|
n = data->index + 1;
|
|
|
|
/* calculate the mean */
|
|
max = data->records[0].y;
|
|
for (i = 1; i < n; i++) {
|
|
if (data->records[i].y > max) {
|
|
max = data->records[i].y;
|
|
}
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
int stats_quantiles_init(stats_quantiles_t * quantiles, int nines)
|
|
{
|
|
if (nines < 2) {
|
|
return -1;
|
|
}
|
|
quantiles->nines = nines;
|
|
/* allocate space for quantiles, starting with 0.99 (two nines) */
|
|
quantiles->quantiles = calloc(sizeof(long), (nines - 1));
|
|
if (!quantiles->quantiles) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int stats_quantiles_free(stats_quantiles_t * quantiles)
|
|
{
|
|
free(quantiles->quantiles);
|
|
return 0;
|
|
}
|
|
|
|
int stats_quantiles_calc(stats_container_t * data,
|
|
stats_quantiles_t * quantiles)
|
|
{
|
|
int i;
|
|
int size;
|
|
int index;
|
|
|
|
// check for sufficient data size of accurate calculation
|
|
if (data->index < 0 ||
|
|
(data->index + 1) < (long)exp10(quantiles->nines)) {
|
|
return -1;
|
|
}
|
|
|
|
size = data->index + 1;
|
|
stats_sort(data, ASCENDING_ON_Y);
|
|
|
|
for (i = 2; i <= quantiles->nines; i++) {
|
|
index = size - size / exp10(i);
|
|
quantiles->quantiles[i - 2] = data->records[index].y;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void stats_quantiles_print(stats_quantiles_t * quantiles)
|
|
{
|
|
int i;
|
|
int fraction = 0;
|
|
for (i = 0; i <= quantiles->nines - 2; i++) {
|
|
if (i > 0)
|
|
fraction += 9 * exp10(i - 1);
|
|
printf("99.%d%% < %ld\n", fraction, quantiles->quantiles[i]);
|
|
}
|
|
}
|
|
|
|
int stats_hist(stats_container_t * hist, stats_container_t * data)
|
|
{
|
|
int i;
|
|
int ret;
|
|
long min, max, width;
|
|
long y, b;
|
|
|
|
ret = 0;
|
|
|
|
if (hist->size <= 0 || data->index < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* calculate the range of dataset */
|
|
min = max = data->records[0].y;
|
|
for (i = 0; i <= data->index; i++) {
|
|
y = data->records[i].y;
|
|
if (y > max)
|
|
max = y;
|
|
if (y < min)
|
|
min = y;
|
|
}
|
|
|
|
/* define the bucket ranges */
|
|
width = MAX((max - min) / hist->size, 1);
|
|
hist->records[0].x = min;
|
|
for (i = 1; i < (hist->size); i++) {
|
|
hist->records[i].x = min + i * width;
|
|
}
|
|
|
|
/* fill in the counts */
|
|
for (i = 0; i <= data->index; i++) {
|
|
y = data->records[i].y;
|
|
b = MIN((y - min) / width, hist->size - 1);
|
|
hist->records[b].y++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void stats_hist_print(stats_container_t * hist)
|
|
{
|
|
long i, x;
|
|
for (i = 0; i < hist->size; i++) {
|
|
x = hist->records[i].x;
|
|
if (i < hist->size - 1)
|
|
printf("[%ld,%ld) = %ld\n", x,
|
|
hist->records[i + 1].x, hist->records[i].y);
|
|
else
|
|
printf("[%ld,-) = %ld\n", x, hist->records[i].y);
|
|
}
|
|
}
|
|
|
|
int stats_container_save(char *filename, char *title, char *xlabel,
|
|
char *ylabel, stats_container_t * data, char *mode)
|
|
{
|
|
int i;
|
|
int minx = 0, maxx = 0, miny = 0, maxy = 0;
|
|
FILE *dat_fd;
|
|
FILE *plt_fd;
|
|
char *datfile;
|
|
char *pltfile;
|
|
stats_record_t *rec;
|
|
|
|
if (!save_stats)
|
|
return 0;
|
|
|
|
/* generate the filenames */
|
|
if (asprintf(&datfile, "%s.dat", filename) == -1) {
|
|
fprintf(stderr,
|
|
"Failed to allocate string for data filename\n");
|
|
return -1;
|
|
}
|
|
if (asprintf(&pltfile, "%s.plt", filename) == -1) {
|
|
fprintf(stderr,
|
|
"Failed to allocate string for plot filename\n");
|
|
return -1;
|
|
}
|
|
|
|
/* generate the data file */
|
|
if ((dat_fd = fopen(datfile, "w")) == NULL) {
|
|
perror("Failed to open dat file");
|
|
return -1;
|
|
} else {
|
|
minx = maxx = data->records[0].x;
|
|
miny = maxy = data->records[0].y;
|
|
for (i = 0; i <= data->index; i++) {
|
|
rec = &data->records[i];
|
|
minx = MIN(minx, rec->x);
|
|
maxx = MAX(maxx, rec->x);
|
|
miny = MIN(miny, rec->y);
|
|
maxy = MAX(maxy, rec->y);
|
|
fprintf(dat_fd, "%ld %ld\n", rec->x, rec->y);
|
|
}
|
|
fclose(dat_fd);
|
|
}
|
|
|
|
/* generate the plt file */
|
|
if (!(plt_fd = fopen(pltfile, "w"))) {
|
|
perror("Failed to open plt file");
|
|
return -1;
|
|
} else {
|
|
fprintf(plt_fd, "set terminal png\n");
|
|
fprintf(plt_fd, "set output \"%s.png\"\n", pltfile);
|
|
fprintf(plt_fd, "set title \"%s\"\n", title);
|
|
fprintf(plt_fd, "set xlabel \"%s\"\n", xlabel);
|
|
fprintf(plt_fd, "set ylabel \"%s\"\n", ylabel);
|
|
fprintf(plt_fd, "plot [0:%d] [0:%d] \"%s\" with %s\n",
|
|
maxx + 1, maxy + 1, datfile, mode);
|
|
fclose(plt_fd);
|
|
}
|
|
|
|
return 0;
|
|
}
|