Data-Histogram-Shared
view release on metacpan or search on metacpan
IV total;
CODE:
/* Range-check + index-compute BEFORE locking so a croak holds no lock. */
if (value < 0)
croak("Data::Histogram::Shared->record: negative value (%lld)", (long long)value);
idx = hist_index_for(h, (int64_t)value);
if (idx < 0)
croak("Data::Histogram::Shared->record: value %lld exceeds highest_trackable_value (%lld)",
(long long)value, (long long)h->hdr->highest);
hist_rwlock_wrlock(h);
hist_record_locked(h, (int64_t)value, (int64_t)count);
total = (IV)h->hdr->total_count;
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
hist_rwlock_wrunlock(h);
RETVAL = total;
OUTPUT:
RETVAL
UV
record_many(self, values)
SV *self
SV **el = av_fetch(av, (SSize_t)i, 0);
IV v = (el && *el) ? SvIV(*el) : 0;
if (v < 0)
croak("Data::Histogram::Shared->record_many: negative value (%lld)", (long long)v);
if (hist_index_for(h, (int64_t)v) < 0)
croak("Data::Histogram::Shared->record_many: value %lld exceeds highest_trackable_value (%lld)",
(long long)v, (long long)h->hdr->highest);
vals[i] = (int64_t)v;
}
}
hist_rwlock_wrlock(h); /* locked region: NO croak-capable calls */
for (i = 0; i < cnt; i++) hist_record_locked(h, vals[i], 1);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED); /* a call always counts, even an empty batch */
hist_rwlock_wrunlock(h);
RETVAL = (UV)cnt;
}
OUTPUT:
RETVAL
IV
value_at_percentile(self, p)
SV *self
double p
PREINIT:
EXTRACT(self);
IV v;
CODE:
hist_rwlock_rdlock(h);
v = (IV)hist_value_at_percentile_locked(h, p);
hist_rwlock_rdunlock(h);
RETVAL = v;
OUTPUT:
RETVAL
IV
count_at_value(self, value)
SV *self
IV value
PREINIT:
RETVAL
double
mean(self)
SV *self
PREINIT:
EXTRACT(self);
double m;
CODE:
hist_rwlock_rdlock(h);
m = hist_mean_locked(h);
hist_rwlock_rdunlock(h);
RETVAL = m;
OUTPUT:
RETVAL
UV
total_count(self)
SV *self
PREINIT:
EXTRACT(self);
hist_rwlock_wrunlock(h);
}
void
reset(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
hist_rwlock_wrlock(h);
hist_reset_locked(h);
__atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
hist_rwlock_wrunlock(h);
IV
lowest(self)
SV *self
PREINIT:
EXTRACT(self);
CODE:
RETVAL = (IV)h->hdr->lowest;
total = h->hdr->total_count;
mn = h->hdr->min_value;
mx = h->hdr->max_value;
counts_len = h->hdr->counts_len;
lowest = h->hdr->lowest;
highest = h->hdr->highest;
sig_figs = h->hdr->sig_figs;
bucket_count = h->hdr->bucket_count;
sub_bucket_count = h->hdr->sub_bucket_count;
ops = h->hdr->stat_ops;
mean = hist_mean_locked(h);
hist_rwlock_rdunlock(h);
HV *hv = newHV();
hv_stores(hv, "lowest", newSViv((IV)lowest));
hv_stores(hv, "highest", newSViv((IV)highest));
hv_stores(hv, "sig_figs", newSViv((IV)sig_figs));
hv_stores(hv, "count", newSViv((IV)total));
hv_stores(hv, "min", newSViv((IV)(total == 0 ? 0 : mn)));
hv_stores(hv, "max", newSViv((IV)mx));
hv_stores(hv, "mean", newSVnv(mean));
static inline void hist_rwlock_spin_pause(void) {
#if defined(__x86_64__) || defined(__i386__)
__asm__ volatile("pause" ::: "memory");
#elif defined(__aarch64__)
__asm__ volatile("yield" ::: "memory");
#else
__asm__ volatile("" ::: "memory");
#endif
}
/* Extract writer PID from rwlock value (lower 31 bits when write-locked). */
#define HIST_RWLOCK_WRITER_BIT 0x80000000U
#define HIST_RWLOCK_PID_MASK 0x7FFFFFFFU
#define HIST_RWLOCK_WR(pid) (HIST_RWLOCK_WRITER_BIT | ((uint32_t)(pid) & HIST_RWLOCK_PID_MASK))
/* Check if a PID is alive. Returns 1 if alive or unknown, 0 if definitely dead. */
/* Liveness via kill(pid,0). NOTE: cannot detect PID reuse -- if a dead
* lock-holder's PID is recycled to an unrelated live process before recovery
* runs, this reports "alive" and that slot's orphaned contribution is not
* reclaimed until the recycled process exits. Robust detection would require
* a per-slot process-start-time epoch (a header-layout/version change).
if (__atomic_compare_exchange_n(lock, &cur, 1,
1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
return;
}
if (__builtin_expect(spin < HIST_RWLOCK_SPIN_LIMIT, 1)) {
hist_rwlock_spin_pause();
continue;
}
hist_park_reader(h);
cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
/* Sleep when write-locked OR when yielding to waiting writers */
if (cur >= HIST_RWLOCK_WRITER_BIT || cur == 0) {
long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
&hist_lock_timeout, NULL, 0);
if (rc == -1 && errno == ETIMEDOUT) {
hist_unpark_reader(h);
hist_recover_after_timeout(h);
spin = 0;
continue;
}
}
if (idx < 0 || idx >= h->hdr->counts_len) return -1;
return idx;
}
/* ================================================================
* HdrHistogram operations (callers hold the lock)
* ================================================================ */
/* Record `count` occurrences of `value`. The XS caller has ALREADY range-
* checked 0 <= value <= highest and idx < counts_len before locking. */
static void hist_record_locked(HistHandle *h, int64_t value, int64_t count) {
int64_t idx = hist_counts_index_for(h, value);
int64_t *counts = hist_counts(h);
counts[idx] += count;
h->hdr->total_count += count;
if (value < h->hdr->min_value) h->hdr->min_value = value;
if (value > h->hdr->max_value) h->hdr->max_value = value;
}
/* Highest equivalent value at or below which `p` percent of recorded values
* lie. Returns 0 for an empty histogram. */
static int64_t hist_value_at_percentile_locked(HistHandle *h, double p) {
int64_t total = h->hdr->total_count;
if (total == 0) return 0;
int64_t want = (int64_t)ceil((p / 100.0) * (double)total);
if (want < 1) want = 1;
if (want > total) want = total;
int64_t *counts = hist_counts(h);
int64_t running = 0;
int64_t len = h->hdr->counts_len;
for (int64_t idx = 0; idx < len; idx++) {
if (!counts[idx]) continue; /* skip empty cells (sparse); a 0 cell can never be the first to reach want */
running += counts[idx];
if (running >= want)
return hist_highest_equiv(h, hist_value_at_index(h, idx));
}
return 0;
}
/* Arithmetic mean of all recorded values (using each bucket's median-equivalent
* value as the representative). Returns 0.0 for an empty histogram. */
static double hist_mean_locked(HistHandle *h) {
int64_t total = h->hdr->total_count;
if (total == 0) return 0.0;
int64_t *counts = hist_counts(h);
int64_t len = h->hdr->counts_len;
double sum = 0.0;
for (int64_t idx = 0; idx < len; idx++) {
int64_t c = counts[idx];
if (c)
sum += (double)c * (double)hist_median_equiv(h, hist_value_at_index(h, idx));
}
* saturating at INT64_MAX on overflow (caller holds dst's write lock) */
static void hist_merge_counts(int64_t *dst, const int64_t *src, int64_t counts_len) {
for (int64_t i = 0; i < counts_len; i++) {
if (src[i] <= 0) continue; /* counts are non-negative; skip empty cells */
if (dst[i] > INT64_MAX - src[i]) dst[i] = INT64_MAX; /* saturate */
else dst[i] += src[i];
}
}
/* reset all counts to 0; reset total/min/max (caller holds the write lock) */
static inline void hist_reset_locked(HistHandle *h) {
memset(hist_counts(h), 0, (size_t)((uint64_t)h->hdr->counts_len * sizeof(int64_t)));
h->hdr->total_count = 0;
h->hdr->min_value = INT64_MAX;
h->hdr->max_value = 0;
}
#endif /* HIST_H */
( run in 0.481 second using v1.01-cache-2.11-cpan-bbe5e583499 )