diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-07 19:39:01 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-07 19:39:01 +0100 |
| commit | 589cb500fa5a6df13fa9634d0071922ec11fce17 (patch) | |
| tree | fa5fc07d6d13d7b241d884441d3d22c1d0189d87 /DESIGN | |
| parent | 1e7248b330bde4c70528ccf91535391e19294dc3 (diff) | |
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.
Diffstat (limited to 'DESIGN')
| -rw-r--r-- | DESIGN | 33 |
1 files changed, 19 insertions, 14 deletions
| @@ -84,24 +84,29 @@ finish a sweep in OT_CLEAN_INTERVAL_MINUTES. At the default bucket count that | |||
| 84 | is one bucket every 117 ms. | 84 | is one bucket every 117 ms. |
| 85 | 85 | ||
| 86 | 86 | ||
| 87 | A torrent carries its hash, two peer-list pointers, a base and a down count, | 87 | A torrent carries its hash, one peerlist pointer, a base and a down count, 40 |
| 88 | 48 bytes on a 64-bit host, and the down count sits in what would otherwise be | 88 | bytes on a 64-bit host. The base and the down count live on the torrent, not |
| 89 | alignment padding. Each family's peer list, 32 bytes plus its peer vector, is | 89 | on a list, so a torrent kept only for its download count holds no list at all. |
| 90 | allocated on that family's first peer and freed when its last peer goes. The | 90 | The one peerlist, allocated on the first peer of either family and freed when |
| 91 | base and the down count live on the torrent rather than on a list, which is | 91 | the last peer of both goes, holds the two family vectors and a single seeder |
| 92 | what lets a list come and go: a torrent kept only for its download count holds | 92 | count. The peer count is not stored, since a vector's own size is the count |
| 93 | no list at all, and a single-family torrent holds one rather than two. | 93 | for a plain list and the sum of its buckets for a split one. That leaves the |
| 94 | seeder count the only tally to maintain, because seeding is a per-peer flag | ||
| 95 | and counting it would mean a walk. | ||
| 94 | 96 | ||
| 95 | This is paid for in the common shape of a large table. A tracker carrying | 97 | This is paid for in the common shape of a large table. A tracker carrying |
| 96 | millions of torrents averages close to two peers each, a few crowded swarms | 98 | millions of torrents averages close to two peers each, a few crowded swarms |
| 97 | over a long tail of near-empty ones, so fixed per-torrent cost, not peer | 99 | over a long tail of near-empty ones, so fixed per-torrent cost, not peer |
| 98 | payload, is most of resident memory. Allocating both lists for every torrent | 100 | payload, is most of resident memory. An earlier layout gave each torrent two |
| 99 | and keeping an idle torrent alive for a day to preserve one counter cost, on | 101 | always-allocated peer lists with their own counters and kept an idle torrent |
| 100 | glibc, 168 bytes per torrent before a peer; the layout above costs 48 for an | 102 | alive for a day to preserve one number: on glibc 168 bytes before a peer. The |
| 101 | empty torrent and 96 for a single-family one. The per-torrent counts are | 103 | merged layout costs 40 for an empty torrent and, on glibc, 136 for a |
| 102 | uint32: no single torrent holds 2^32 peers, and the four bytes saved against a | 104 | single-family one and 184 for a torrent carrying both. A torrent with peers in |
| 103 | size_t are multiplied by the torrent count. Global totals stay 64-bit. | 105 | both families now takes one allocation rather than two. The split is drawn |
| 104 | 106 | along the family distribution: empty and both-family torrents gain, a | |
| 107 | single-family torrent pays for the absent family's vector header. On jemalloc | ||
| 108 | the bins differ, so the per-shape figures do too, but the empty-torrent and | ||
| 109 | single-allocation gains hold on both. | ||
| 105 | 110 | ||
| 106 | A refused announce is answered over UDP with a well formed announce for an | 111 | A refused announce is answered over UDP with a well formed announce for an |
| 107 | empty swarm rather than with the error action. BEP 15 binds a client to the | 112 | empty swarm rather than with the error action. BEP 15 binds a client to the |
