From 8f520e52d7d4081a0835374c7a448116baac64ab Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 2 Nov 2026 20:39:26 +0100 Subject: Turn the bucket count into a knob, but keep cleaner wake-ups at a constant rate --- DESIGN | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'DESIGN') diff --git a/DESIGN b/DESIGN index ff37706..5abeb4a 100644 --- a/DESIGN +++ b/DESIGN @@ -16,7 +16,7 @@ A connection carries one request and closes. Keep-alive works and is rarely asked for. Statistics are approximate on purpose. Counters are per thread and summed -when read, so a /stats read can under-report once. Nothing drifts. +when read, so a /stats read can under-report once. Decisions @@ -60,9 +60,44 @@ connections, the filter serves more requests than no filter, not fewer, so it is not the denial of service vector that FreeBSD-SA-02:26 once described. +The torrent table is split into 2^OT_BUCKET_COUNT_BITS buckets, 1024 by +default, set from the Makefile. Inserting a torrent opens a slot in a sorted +vector, so the bytes moved grow with the bucket's occupancy. Insert cost per +torrent, Linux: + + 2^10 2^12 2^14 2^16 + 200k 466 ns 439 ns + 1M 834 ns 579 ns 529 ns + 4M 2348 ns 1163 ns 770 ns 674 ns + +Re-announcing an existing torrent moves by under ten per cent over the same +range, so the cost is paid while the torrent population is built and not in +steady state. Below a few hundred thousand torrents there is nothing to win. +2^14 captures most of the gain for two per cent more resident memory. + +The cleaner's wake rate does not follow the bucket count. A cap of ten wakes +a second sets the sleep, and a wake cleans as many buckets as it must to +finish a sweep in OT_CLEAN_INTERVAL_MINUTES. At the default bucket count that +is one bucket every 117 ms. + + Paths not taken --------------- +A keyed hash for the bucket index. Info hashes are SHA-1 output and already +uniform, so it changes no distribution. It would buy resistance to deliberate +bucket flooding and cost the ordering a full scrape depends on: buckets are +ordered by hash prefix and sorted within, so walking them emits the sorted +dictionary bencode wants, with no sort and no copy. + +Cache line padding for the per-bucket locks and vector headers. The +contention it was meant to remove is not there. On glibc, where a 48-byte +mutex straddles a line, padding gains 3 to 8 per cent, but gains as much at +one thread where no false sharing can exist, and scaling from one to eight +threads is identical either way. On FreeBSD, where eight 8-byte mutexes share +a line and the risk should be highest, padding is 15 per cent slower at one +thread and neutral to 6 per cent slower at four and eight. + accf_http, the HTTP-aware accept filter. It duplicates a parser the tracker has anyway, puts more kernel code in the path, and holds a connection that sends "GET /" and stops, which accf_data hands straight over. -- cgit v1.3