Files
sysbench/src/sb_histogram.c
Alexey Kopytov 05f2a8e87b Fix #118: Build fails on i686 when using system ck
Provide compatibility wrappers for system (i.e. distribution-provided)
ConcurrencyKit on i386. Unlike bundled ConcurrencyKit, older versions
that may be provided by distributions do not support CK_USE_CC_BUILTINS,
which makes implementing 64-bit atomics impossible on x86 (32-bit).

Detect if 64-bit atomics are not provided at compile stage and if so,
resort to GCC builtin implementations.
2017-03-13 14:38:19 +03:00

374 lines
8.4 KiB
C

/* Copyright (C) 2011-2017 Alexey Kopytov.
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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef _WIN32
#include "sb_win.h"
#endif
#ifdef STDC_HEADERS
# include <stdio.h>
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_MATH_H
# include <math.h>
#endif
#include "sysbench.h"
#include "sb_histogram.h"
#include "sb_logger.h"
#include "sb_rand.h"
#include "sb_ck_pr.h"
#include "ck_cc.h"
#include "sb_util.h"
/*
Number of slots for current histogram array. TODO: replace this constant with
some autodetection code based on the number of available cores or some such.
*/
#define SB_HISTOGRAM_NSLOTS 128
/* Global latency histogram */
sb_histogram_t sb_latency_histogram CK_CC_CACHELINE;
int sb_histogram_init(sb_histogram_t *h, size_t size,
double range_min, double range_max)
{
size_t i;
uint64_t *tmp;
/* Allocate memory for cumulative_array + temp_array + all slot arrays */
tmp = (uint64_t *) calloc(size * (SB_HISTOGRAM_NSLOTS + 2), sizeof(uint64_t));
h->interm_slots = (uint64_t **) malloc(SB_HISTOGRAM_NSLOTS *
sizeof(uint64_t *));
if (tmp == NULL || h->interm_slots == NULL)
{
log_text(LOG_FATAL,
"Failed to allocate memory for a histogram object, size = %zd",
size);
return 1;
}
h->cumulative_array = tmp;
tmp += size;
h->temp_array = tmp;
tmp += size;
for (i = 0; i < SB_HISTOGRAM_NSLOTS; i++)
{
h->interm_slots[i] = tmp;
tmp += size;
}
h->range_deduct = log(range_min);
h->range_mult = (size - 1) / (log(range_max) - h->range_deduct);
h->range_min = range_min;
h->range_max = range_max;
h->array_size = size;
pthread_rwlock_init(&h->lock, NULL);
return 0;
}
void sb_histogram_update(sb_histogram_t *h, double value)
{
size_t slot;
ssize_t i;
slot = sb_rand_uniform_uint64() % SB_HISTOGRAM_NSLOTS;
i = floor((log(value) - h->range_deduct) * h->range_mult + 0.5);
if (SB_UNLIKELY(i < 0))
i = 0;
else if (SB_UNLIKELY(i >= (ssize_t) (h->array_size)))
i = h->array_size - 1;
ck_pr_inc_64(&h->interm_slots[slot][i]);
}
double sb_histogram_get_pct_intermediate(sb_histogram_t *h,
double percentile)
{
size_t i, s;
uint64_t nevents, ncur, nmax;
double res;
nevents = 0;
/*
This can be called concurrently with other sb_histogram_get_pct_*()
functions, so use the lock to protect shared structures. This will not block
sb_histogram_update() calls, but we make sure we don't lose any concurrent
increments by atomically fetching each array element and replacing it with
0.
*/
pthread_rwlock_wrlock(&h->lock);
/*
Merge intermediate slots into temp_array.
*/
const size_t size = h->array_size;
uint64_t * const array = h->temp_array;
for (i = 0; i < size; i++)
{
array[i] = ck_pr_fas_64(&h->interm_slots[0][i], 0);
nevents += array[i];
}
for (s = 1; s < SB_HISTOGRAM_NSLOTS; s++)
{
for (i = 0; i < size; i++)
{
uint64_t t;
t = ck_pr_fas_64(&h->interm_slots[s][i], 0);
array[i] += t;
nevents += t;
}
}
/*
Now that we have an aggregate 'snapshot' of current arrays and the total
number of events in it, calculate the current, intermediate percentile value
to return.
*/
nmax = floor(nevents * percentile / 100 + 0.5);
ncur = 0;
for (i = 0; i < size; i++)
{
ncur += array[i];
if (ncur >= nmax)
break;
}
res = exp(i / h->range_mult + h->range_deduct);
/* Finally, add temp_array into accumulated values in cumulative_array. */
for (i = 0; i < size; i++)
{
h->cumulative_array[i] += array[i];
}
h->cumulative_nevents += nevents;
pthread_rwlock_unlock(&h->lock);
return res;
}
/*
Aggregate arrays from intermediate slots into cumulative_array. This should be
called with the histogram lock write-locked.
*/
static void merge_intermediate_into_cumulative(sb_histogram_t *h)
{
size_t i, s;
uint64_t nevents;
nevents = h->cumulative_nevents;
const size_t size = h->array_size;
uint64_t * const array = h->cumulative_array;
for (s = 0; s < SB_HISTOGRAM_NSLOTS; s++)
{
for (i = 0; i < size; i++)
{
uint64_t t = ck_pr_fas_64(&h->interm_slots[s][i], 0);
array[i] += t;
nevents += t;
}
}
h->cumulative_nevents = nevents;
}
/*
Calculate a given percentile from the cumulative array. This should be called
with the histogram lock either read- or write-locked.
*/
static double get_pct_cumulative(sb_histogram_t *h, double percentile)
{
size_t i;
uint64_t ncur, nmax;
nmax = floor(h->cumulative_nevents * percentile / 100 + 0.5);
ncur = 0;
for (i = 0; i < h->array_size; i++)
{
ncur += h->cumulative_array[i];
if (ncur >= nmax)
break;
}
return exp(i / h->range_mult + h->range_deduct);
}
double sb_histogram_get_pct_cumulative(sb_histogram_t *h, double percentile)
{
double res;
/*
This can be called concurrently with other sb_histogram_get_pct_*()
functions, so use the lock to protect shared structures. This will not block
sb_histogram_update() calls, but we make sure we don't lose any concurrent
increments by atomically fetching each array element and replacing it with
0.
*/
pthread_rwlock_wrlock(&h->lock);
merge_intermediate_into_cumulative(h);
res = get_pct_cumulative(h, percentile);
pthread_rwlock_unlock(&h->lock);
return res;
}
double sb_histogram_get_pct_checkpoint(sb_histogram_t *h,
double percentile)
{
double res;
/*
This can be called concurrently with other sb_histogram_get_pct_*()
functions, so use the lock to protect shared structures. This will not block
sb_histogram_update() calls, but we make sure we don't lose any concurrent
increments by atomically fetching each array element and replacing it with
0.
*/
pthread_rwlock_wrlock(&h->lock);
merge_intermediate_into_cumulative(h);
res = get_pct_cumulative(h, percentile);
/* Reset the cumulative array */
memset(h->cumulative_array, 0, h->array_size * sizeof(uint64_t));
h->cumulative_nevents = 0;
pthread_rwlock_unlock(&h->lock);
return res;
}
void sb_histogram_print(sb_histogram_t *h)
{
uint64_t maxcnt;
int width;
size_t i;
pthread_rwlock_wrlock(&h->lock);
merge_intermediate_into_cumulative(h);
uint64_t * const array = h->cumulative_array;
maxcnt = 0;
for (i = 0; i < h->array_size; i++)
{
if (array[i] > maxcnt)
maxcnt = array[i];
}
if (maxcnt == 0)
return;
printf(" value ------------- distribution ------------- count\n");
for (i = 0; i < h->array_size; i++)
{
if (array[i] == 0)
continue;
width = floor(array[i] * (double) 40 / maxcnt + 0.5);
printf("%12.3f |%-40.*s %lu\n",
exp(i / h->range_mult + h->range_deduct), /* value */
width, "****************************************", /* distribution */
(unsigned long) array[i]); /* count */
}
pthread_rwlock_unlock(&h->lock);
}
void sb_histogram_done(sb_histogram_t *h)
{
pthread_rwlock_destroy(&h->lock);
free(h->cumulative_array);
free(h->interm_slots);
}
/*
Allocate a new histogram and initialize it with sb_histogram_init().
*/
sb_histogram_t *sb_histogram_new(size_t size, double range_min,
double range_max)
{
sb_histogram_t *h;
if ((h = malloc(sizeof(*h))) == NULL)
return NULL;
if (sb_histogram_init(h, size, range_min, range_max))
{
free(h);
return NULL;
}
return h;
}
/*
Deallocate a histogram allocated with sb_histogram_new().
*/
void sb_histogram_delete(sb_histogram_t *h)
{
sb_histogram_done(h);
free(h);
}