Even though statistics are lockless they still use atomics which might cause contention. Split stats counters into buckets, sharded by an artificial thread identifier, to increase throughput.
214 lines
5.3 KiB
C
214 lines
5.3 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
|
|
/*! \file */
|
|
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
|
|
#include <isc/atomic.h>
|
|
#include <isc/buffer.h>
|
|
#include <isc/magic.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/platform.h>
|
|
#include <isc/print.h>
|
|
#include <isc/refcount.h>
|
|
#include <isc/stats.h>
|
|
#include <isc/thread.h>
|
|
#include <isc/util.h>
|
|
|
|
#define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't')
|
|
#define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
|
|
|
|
#define STATS_BUCKETS 64
|
|
|
|
#if defined(_WIN32) && !defined(_WIN64)
|
|
typedef atomic_int_fast32_t isc__atomic_statcounter_t;
|
|
#else
|
|
typedef atomic_int_fast64_t isc__atomic_statcounter_t;
|
|
#endif
|
|
|
|
struct isc_stats {
|
|
unsigned int magic;
|
|
isc_mem_t *mctx;
|
|
isc_refcount_t references;
|
|
int ncounters;
|
|
isc__atomic_statcounter_t *counters;
|
|
};
|
|
|
|
ISC_THREAD_LOCAL int isc__stats_thread_v = -1;
|
|
static atomic_uint_fast32_t isc__stats_thread_n = 0;
|
|
|
|
static int
|
|
threadhash() {
|
|
if (isc__stats_thread_v < 0) {
|
|
isc__stats_thread_v = atomic_fetch_add_relaxed(&isc__stats_thread_n, 1) % STATS_BUCKETS;
|
|
}
|
|
return (isc__stats_thread_v);
|
|
}
|
|
|
|
static isc_result_t
|
|
create_stats(isc_mem_t *mctx, int ncounters, isc_stats_t **statsp) {
|
|
isc_stats_t *stats;
|
|
size_t counters_alloc_size;
|
|
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
stats = isc_mem_get(mctx, sizeof(*stats));
|
|
counters_alloc_size = sizeof(isc__atomic_statcounter_t) * ncounters * STATS_BUCKETS;
|
|
stats->counters = isc_mem_get(mctx, counters_alloc_size);
|
|
isc_refcount_init(&stats->references, 1);
|
|
memset(stats->counters, 0, counters_alloc_size);
|
|
stats->mctx = NULL;
|
|
isc_mem_attach(mctx, &stats->mctx);
|
|
stats->ncounters = ncounters;
|
|
stats->magic = ISC_STATS_MAGIC;
|
|
*statsp = stats;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
void
|
|
isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) {
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
isc_refcount_increment(&stats->references);
|
|
*statsp = stats;
|
|
}
|
|
|
|
void
|
|
isc_stats_detach(isc_stats_t **statsp) {
|
|
isc_stats_t *stats;
|
|
|
|
REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp));
|
|
|
|
stats = *statsp;
|
|
*statsp = NULL;
|
|
|
|
if (isc_refcount_decrement(&stats->references) == 1) {
|
|
isc_refcount_destroy(&stats->references);
|
|
isc_mem_put(stats->mctx, stats->counters,
|
|
sizeof(isc__atomic_statcounter_t) *
|
|
stats->ncounters *
|
|
STATS_BUCKETS);
|
|
isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
|
|
}
|
|
}
|
|
|
|
int
|
|
isc_stats_ncounters(isc_stats_t *stats) {
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
|
|
return (stats->ncounters);
|
|
}
|
|
|
|
isc_result_t
|
|
isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
return (create_stats(mctx, ncounters, statsp));
|
|
}
|
|
|
|
void
|
|
isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) {
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
REQUIRE(counter < stats->ncounters);
|
|
int idx = threadhash() * stats->ncounters + counter;
|
|
atomic_fetch_add_explicit(&stats->counters[idx], 1,
|
|
memory_order_relaxed);
|
|
}
|
|
|
|
void
|
|
isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) {
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
REQUIRE(counter < stats->ncounters);
|
|
|
|
int idx = threadhash() * stats->ncounters + counter;
|
|
atomic_fetch_sub_explicit(&stats->counters[idx], 1,
|
|
memory_order_relaxed);
|
|
}
|
|
|
|
void
|
|
isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn,
|
|
void *arg, unsigned int options)
|
|
{
|
|
int i;
|
|
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
|
|
for (i = 0; i < stats->ncounters; i++) {
|
|
uint32_t counter = 0;
|
|
int b;
|
|
for (b = 0; b < STATS_BUCKETS; b++) {
|
|
int idx = stats->ncounters * b + i;
|
|
counter += atomic_load_explicit(&stats->counters[idx],
|
|
memory_order_relaxed);
|
|
}
|
|
if ((options & ISC_STATSDUMP_VERBOSE) == 0 && counter == 0) {
|
|
continue;
|
|
}
|
|
dump_fn((isc_statscounter_t)i, counter, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_stats_set(isc_stats_t *stats, uint64_t val,
|
|
isc_statscounter_t counter)
|
|
{
|
|
int i;
|
|
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
REQUIRE(counter < stats->ncounters);
|
|
atomic_store_explicit(&stats->counters[counter], val,
|
|
memory_order_relaxed);
|
|
for (i = 1; i < STATS_BUCKETS; i++) {
|
|
int idx = stats->ncounters * i + counter;
|
|
atomic_store_explicit(&stats->counters[idx], val,
|
|
memory_order_relaxed);
|
|
}
|
|
}
|
|
|
|
void isc_stats_update_if_greater(isc_stats_t *stats,
|
|
isc_statscounter_t counter,
|
|
isc_statscounter_t value)
|
|
{
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
REQUIRE(counter < stats->ncounters);
|
|
|
|
isc_statscounter_t curr_value;
|
|
do {
|
|
curr_value = atomic_load_relaxed(&stats->counters[counter]);
|
|
if (curr_value >= value) {
|
|
break;
|
|
}
|
|
} while (!atomic_compare_exchange_strong(&stats->counters[counter],
|
|
&curr_value,
|
|
value));
|
|
}
|
|
|
|
isc_statscounter_t
|
|
isc_stats_get_counter(isc_stats_t *stats, isc_statscounter_t counter)
|
|
{
|
|
uint32_t value = 0;
|
|
int i;
|
|
REQUIRE(ISC_STATS_VALID(stats));
|
|
REQUIRE(counter < stats->ncounters);
|
|
|
|
for (i = 0; i < STATS_BUCKETS; i++) {
|
|
int idx = i * stats->ncounters + counter;
|
|
value += atomic_load_explicit(&stats->counters[idx],
|
|
memory_order_relaxed);
|
|
}
|
|
return (value);
|
|
}
|