Data-Histogram-Shared

 view release on metacpan or  search on metacpan

Shared.xs  view on Meta::CPAN

    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

Shared.xs  view on Meta::CPAN

                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:

Shared.xs  view on Meta::CPAN

    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);

Shared.xs  view on Meta::CPAN

        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;

Shared.xs  view on Meta::CPAN

        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));

hist.h  view on Meta::CPAN

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).

hist.h  view on Meta::CPAN

            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;
            }
        }

hist.h  view on Meta::CPAN

    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));
    }

hist.h  view on Meta::CPAN

 * 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 )