ratelimit,sd-journal: don't read the clock once per journal file per entry

sd_journal_next() sweeps every open journal file to find the next entry, and
next_beyond_location() rate-limits its tail timestamp refresh per file. Since
ratelimit_below() reads the clock itself, iterating a journal spread over a
hundred files meant a hundred clock_gettime() calls for every single entry,
nearly all of them only to conclude that the one second interval had not
elapsed yet. On a 730M journal in 100 files this was the single largest cost in
journalctl, over a fifth of the run.

Add ratelimit_below_at(), which takes the timestamp from the caller, and read
the clock once per sweep in real_journal_next() instead. ratelimit_below()
keeps its behaviour and becomes a wrapper.

The timestamp is now sampled once for the whole sweep rather than once per
file. The interval being rate-limited is a second and a sweep takes
microseconds, so this makes no practical difference.

On that journal, journalctl -o export goes from 4.02s to 2.93s, and the default
journalctl -o short from 3.30s to 2.14s.
This commit is contained in:
Armaan Sandhu
2026-09-23 08:59:56 +09:00
committed by Yu Watanabe
parent 7461360c15
commit 79e32e7c73
3 changed files with 23 additions and 8 deletions
+11 -5
View File
@@ -6,16 +6,12 @@
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
bool ratelimit_below(RateLimit *rl) {
usec_t ts;
bool ratelimit_below_at(RateLimit *rl, usec_t ts) {
assert(rl);
if (!ratelimit_configured(rl))
return true;
ts = now(CLOCK_BOOTTIME);
if (rl->begin <= 0 ||
usec_sub_unsigned(ts, rl->begin) > rl->interval) {
rl->begin = ts; /* Start a new time window */
@@ -30,6 +26,16 @@ bool ratelimit_below(RateLimit *rl) {
return rl->num <= rl->burst;
}
bool ratelimit_below(RateLimit *rl) {
assert(rl);
if (!ratelimit_configured(rl)) /* Keep this check here too: the argument below is evaluated
* before the callee could short-circuit on it. */
return true;
return ratelimit_below_at(rl, now(CLOCK_BOOTTIME));
}
unsigned ratelimit_num_dropped(const RateLimit *rl) {
assert(rl);
+5
View File
@@ -21,6 +21,11 @@ static inline bool ratelimit_configured(const RateLimit *rl) {
}
bool ratelimit_below(RateLimit *rl);
/* Like ratelimit_below(), but takes the current CLOCK_BOOTTIME timestamp from the caller, for callers
* that check many rate limits in a row and should not read the clock for each of them. The timestamps
* passed for a given RateLimit must not go backwards: a ts before the one that opened the current
* window makes usec_sub_unsigned() return 0, i.e. the window looks like it just started. */
bool ratelimit_below_at(RateLimit *rl, usec_t ts);
unsigned ratelimit_num_dropped(const RateLimit *rl);