From 589cb500fa5a6df13fa9634d0071922ec11fce17 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sat, 7 Nov 2026 19:39:01 +0100 Subject: Unify the peerlists for v4 and v6 This commit finishes what started with unifying v4 and v6 trackers, from then on, torrents carried a peer list per family, with obvious redundancies. With the race to save memory, these became burdening enough that now these peerlist pointers have been moved into a single struct, holding both v4 and v6 peers in their respective ot_vectors, so the per- torrent cost shrunk by another pointer's size. Each peerlist also lost its peer count cache, as in most cases, it's trivial to load it directly from the ot_vector's entry count. The seed counts that were kept per family only to be aggregated later for every consumer, are also merged now. Total savings depend on the tracker's actual load. In some corner cases, where only one address family dominates the torrents, we actually use a few bytes more in the ot_vector for the absent family, but even that mostly becomes noise: before it actually adds to the memory load, the peer payload will be the deciding factor. Most wins are on what often is experienced on trackers: A long tail of abandoned torrents now needs eight fewer bytes, slipping into a smaller allocation unit on glibc's allocator. --- DESIGN | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'DESIGN') diff --git a/DESIGN b/DESIGN index a32eeb8..f089a3d 100644 --- a/DESIGN +++ b/DESIGN @@ -84,24 +84,29 @@ finish a sweep in OT_CLEAN_INTERVAL_MINUTES. At the default bucket count that is one bucket every 117 ms. -A torrent carries its hash, two peer-list pointers, a base and a down count, -48 bytes on a 64-bit host, and the down count sits in what would otherwise be -alignment padding. Each family's peer list, 32 bytes plus its peer vector, is -allocated on that family's first peer and freed when its last peer goes. The -base and the down count live on the torrent rather than on a list, which is -what lets a list come and go: a torrent kept only for its download count holds -no list at all, and a single-family torrent holds one rather than two. +A torrent carries its hash, one peerlist pointer, a base and a down count, 40 +bytes on a 64-bit host. The base and the down count live on the torrent, not +on a list, so a torrent kept only for its download count holds no list at all. +The one peerlist, allocated on the first peer of either family and freed when +the last peer of both goes, holds the two family vectors and a single seeder +count. The peer count is not stored, since a vector's own size is the count +for a plain list and the sum of its buckets for a split one. That leaves the +seeder count the only tally to maintain, because seeding is a per-peer flag +and counting it would mean a walk. This is paid for in the common shape of a large table. A tracker carrying millions of torrents averages close to two peers each, a few crowded swarms over a long tail of near-empty ones, so fixed per-torrent cost, not peer -payload, is most of resident memory. Allocating both lists for every torrent -and keeping an idle torrent alive for a day to preserve one counter cost, on -glibc, 168 bytes per torrent before a peer; the layout above costs 48 for an -empty torrent and 96 for a single-family one. The per-torrent counts are -uint32: no single torrent holds 2^32 peers, and the four bytes saved against a -size_t are multiplied by the torrent count. Global totals stay 64-bit. - +payload, is most of resident memory. An earlier layout gave each torrent two +always-allocated peer lists with their own counters and kept an idle torrent +alive for a day to preserve one number: on glibc 168 bytes before a peer. The +merged layout costs 40 for an empty torrent and, on glibc, 136 for a +single-family one and 184 for a torrent carrying both. A torrent with peers in +both families now takes one allocation rather than two. The split is drawn +along the family distribution: empty and both-family torrents gain, a +single-family torrent pays for the absent family's vector header. On jemalloc +the bins differ, so the per-shape figures do too, but the empty-torrent and +single-allocation gains hold on both. A refused announce is answered over UDP with a well formed announce for an empty swarm rather than with the error action. BEP 15 binds a client to the -- cgit v1.3