# The rate ceiling

Five measured runs at 10, 50 and 100 Hz on an RB5009 — all of them lossless — and what sampling faster actually buys.

Source: https://jmrplens.github.io/mikroscope/cost/rate-ceiling/

The question this page answers is the one worth asking before trusting any of
it: **how fast can it sample before it starts losing data?**

On the reference device the answer is that it does not, up to the CLI's own
100 Hz cap, with every source read on every tick. That is not an extrapolation
from the 10 Hz figure. It is five runs.

## The five runs

Every figure is from the agent's own cgroup and `/metrics`, with the full source set. Each row is
one window with the ring already full:

Measured on RB5009UG+S+ · 4 × 1.4 GHz Cortex-A72 · RouterOS 7.24.2 · 2026-09-15 · 60 s windows at steady state (ring full), full source set, collector forwarding to a file, a Prometheus exposition and InfluxDB 3 at once

The measured runs:

| rate | floors | CPU of one core | µs/sample | RSS | slipped ticks | gaps / drops |
| --- | --- | --- | --- | --- | --- | --- |
| 10 Hz (default) | default | **2.85 %** | 2 856 | 31.3 MiB | **0** | 0 / 0 |
| 50 Hz | default | **10.13 %** | 2 026 | 51.9 MiB | **0** | 0 / 0 |
| 100 Hz | default | **17.81 %** | 1 781 | 76.5 MiB | 5 (0.08 %) | 0 / 0 |
| 50 Hz | `FLOOR_HZ=50` | **22.47 %** | 4 494 | 60.6 MiB | 6 (0.20 %) | 0 / 0 |
| 100 Hz | `FLOOR_HZ=100` | **43.95 %** | 4 395 | 79.6 MiB | 14 (0.23 %) | 0 / 0 |

`FLOOR_HZ` means every source is read on every tick, with no per-source floor at
all — the worst case the agent can be asked for.

Memory differs by row because the ring does. The 10 Hz row is the install
default (300 s ring, `--mem-limit-mb 40 --memory-max 64M`); the 50 Hz rows used `--buffer 120 --mem-limit-mb 64 --memory-max 96M` and
the 100 Hz rows `--buffer 120 --mem-limit-mb 80 --memory-max 128M`. Give the agent's garbage collector room or the cost
jumps for reasons that have nothing to do with the rate.

> **Nothing was lost at any rate, in any configuration**
>
> Sampled seconds covered wall clock to 1.0000 in all five runs, the delivered rate was the
> configured rate to three figures, and every sink — file, Prometheus, InfluxDB — reported 0 gaps
> and 0 drops.

## Two things in that table worth reading twice

### Per-sample cost falls as the rate rises

A sample costs 2 856 µs at 10 Hz against 1 781 µs at 100 Hz. That is not a
paradox, it is the floors working: the expensive sources are amortised over more samples.
`/proc/slabinfo`, one of the expensive sources (13.8 kB), is read every 2nd tick at 10 Hz and every
17th at 100 Hz, so an average sample costs less while the _rate_ of slabinfo reads
stays near its 6 Hz floor either way (5 Hz at 10 Hz, about 5.9 Hz at 100 Hz).

With `FLOOR_HZ` there is no amortisation to be had and the per-sample cost is
flat — 4 494 µs at 50 Hz and 4 395 µs at
100 Hz — so CPU scales linearly with the rate: 22.47 %, then 43.95 %.

### A slipped tick is not a lost sample

The sample is still produced and still delivered, carrying its real `dt_ns`, so
any rate computed from it stays correct. It is a smear, not a hole, and
`mikroscope_tick_interval_seconds` is where you see it. At 100 Hz with
everything on every tick, 99.5 % of ticks still landed within 11 ms of a 10 ms
period and the worst was 15 ms.

## What binds is the read, not the CPU

At the default floors a whole tick's sources are read in under 2 ms for 97.5 % of
samples at 100 Hz, comfortably inside a 10 ms period. `FLOOR_HZ` pushes 1.4 %
of reads past 5 ms, and those are the ticks that slip. The CPU headroom is
larger than the timing headroom, which is why the ceiling is a statement about
I/O rather than about the A72.

## What sampling faster actually buys

Not CPU-percent resolution. The jiffie is 10 ms, so at 100 Hz a sample holds
either 0 or 1 busy tick and the per-sample busy ratio has two possible values.
Above roughly 20 Hz the tick counters stop being a percentage and become an
occupancy indicator; the PMU is the resolution from there.

What a higher rate does buy is everything that is not jiffie-quantised — softnet
packet counts, interrupt deltas, PMU counters, the kernel log's own timestamps —
and a tighter bound on how long a burst can hide between two samples.

> **Not measured, so not claimed**
>
> Any rate on a board that is not this one, and what happens under a traffic load heavier than this
> router's ordinary evening — about 30 Mbit/s. Before quoting a number for your
> device, re-measure it there: two `/metrics` reads 60 s apart at steady state.

## See also

- [The cost of the observer](/mikroscope/cost/): the budget these runs are measured against, and the
  three commands to measure it yourself.
- [What the numbers do not say](/mikroscope/cost/limits/): what `/metrics` can and cannot recover
  at any of these rates.
