diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-09 19:29:55 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-09 19:29:55 +0100 |
| commit | 4bdec357c08d2919028230360470df9f41c11a97 (patch) | |
| tree | 54bbd746472828aeb38d6f2cb0cc067f7071015b | |
| parent | 4eff65c7e7a3a0d227358f7dc3932b3004a99a15 (diff) | |
Add memory access pattern overview to the DESIGN document
| -rw-r--r-- | DESIGN | 38 |
1 files changed, 35 insertions, 3 deletions
| @@ -108,6 +108,32 @@ 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 | 108 | the bins differ, so the per-shape figures do too, but the empty-torrent and |
| 109 | single-allocation gains hold on both. | 109 | single-allocation gains hold on both. |
| 110 | 110 | ||
| 111 | Reading those structs for one announce is a walk down cold memory. With the | ||
| 112 | hash in hand, the steady-state path to an existing torrent reads, in order: | ||
| 113 | |||
| 114 | step reads load | ||
| 115 | lock one bucket mutex cold | ||
| 116 | bucket header that bucket's torrent vector cold | ||
| 117 | torrent search a hash per probe, log2 of the bucket cold | ||
| 118 | peerlist the torrent's peerlist cold | ||
| 119 | peer search a peer per probe, log2 of the swarm cold | ||
| 120 | reply counts the peerlist's sizes and seeder count hot | ||
| 121 | |||
| 122 | Each cold line is a separate allocation, and on a large table each is a miss. | ||
| 123 | The hot line rode in on the peerlist step before it. The torrent search | ||
| 124 | dominates, one miss per probe into a bucket of hundreds of thousands, with the | ||
| 125 | peerlist pointer chase and the peer search behind it. Aging the torrent adds | ||
| 126 | nothing in steady state: one touched this minute has a zero span and the | ||
| 127 | cleaner returns at once. The reply counts are hot because the two family | ||
| 128 | sizes and the seeder count share the peerlist's line. A split swarm is the | ||
| 129 | exception, since summing its size walks the bucket headers, but those are the | ||
| 130 | largest torrents, where handing out peers already walks the swarm. | ||
| 131 | |||
| 132 | A request waits on two system calls around this, a receive and a send, each | ||
| 133 | on the order of a handful of misses. Memory latency and kernel time are | ||
| 134 | co-dominant. Neither hides behind the other, and no term in the table is one | ||
| 135 | a cache would hold still. | ||
| 136 | |||
| 111 | A refused announce is answered over UDP with a well formed announce for an | 137 | A refused announce is answered over UDP with a well formed announce for an |
| 112 | empty swarm rather than with the error action. BEP 15 binds a client to the | 138 | empty swarm rather than with the error action. BEP 15 binds a client to the |
| 113 | interval it is handed and says nothing about what a client should do with an | 139 | interval it is handed and says nothing about what a client should do with an |
| @@ -140,7 +166,7 @@ Build hardening the shipped binary | |||
| 140 | ---------------------------------- | 166 | ---------------------------------- |
| 141 | 167 | ||
| 142 | The production and debug binaries carry -fstack-protector-strong, -fPIE and | 168 | The production and debug binaries carry -fstack-protector-strong, -fPIE and |
| 143 | _FORTIFY_SOURCE=2 on every platform; the GNU-style linkers also get -pie and | 169 | _FORTIFY_SOURCE=2 on every platform. The GNU-style linkers also get -pie and |
| 144 | full RELRO (-z relro,-z now). Measured cost on | 170 | full RELRO (-z relro,-z now). Measured cost on |
| 145 | the announce hot path is 300 to 305 nanoseconds, under two per cent, and the | 171 | the announce hot path is 300 to 305 nanoseconds, under two per cent, and the |
| 146 | individual flags sit inside the run-to-run noise: add_peer has few stack | 172 | individual flags sit inside the run-to-run noise: add_peer has few stack |
| @@ -152,7 +178,7 @@ The link flags -pie and RELRO are gated on the operating system: Apple's | |||
| 152 | linker rejects -z, and flags a bare -pie in the combined compile-and-link as | 178 | linker rejects -z, and flags a bare -pie in the combined compile-and-link as |
| 153 | unused, while macOS links executables PIE by default, so leaving both off | 179 | unused, while macOS links executables PIE by default, so leaving both off |
| 154 | there costs no hardening. The branch runs in the shell through a != assignment, | 180 | there costs no hardening. The branch runs in the shell through a != assignment, |
| 155 | the one form both bmake and GNU make 4 accept; $(shell) is GNU only and GNU | 181 | the one form both bmake and GNU make 4 accept. $(shell) is GNU only and GNU |
| 156 | make 3.81, which stock macOS ships, has neither. On that old make the != line | 182 | make 3.81, which stock macOS ships, has neither. On that old make the != line |
| 157 | is inert and leaves the flags empty, which is the right answer for macOS | 183 | is inert and leaves the flags empty, which is the right answer for macOS |
| 158 | anyway, so every platform resolves correctly without a make conditional. | 184 | anyway, so every platform resolves correctly without a make conditional. |
| @@ -225,13 +251,19 @@ A directory for object files. bmake has no pattern rules and the Makefile | |||
| 225 | builds under FreeBSD's make today. Building each binary in one invocation | 251 | builds under FreeBSD's make today. Building each binary in one invocation |
| 226 | clears the objects out of the tree without giving that up. | 252 | clears the objects out of the tree without giving that up. |
| 227 | 253 | ||
| 254 | A cached peer count on the torrent or the peerlist. The count is a vector's | ||
| 255 | own size, a field in a line the peer insert has already loaded, so a cache | ||
| 256 | would hold a second copy of a number already in reach and have to be kept in | ||
| 257 | step with the vector it copies. Only a split swarm pays, to sum its buckets, | ||
| 258 | and there the sum is lost against walking the swarm to answer at all. | ||
| 259 | |||
| 228 | 260 | ||
| 229 | 261 | ||
| 230 | Platform differences that cost us code | 262 | Platform differences that cost us code |
| 231 | -------------------------------------- | 263 | -------------------------------------- |
| 232 | 264 | ||
| 233 | SO_REUSEPORT means three different things. Linux spreads datagrams over the | 265 | SO_REUSEPORT means three different things. Linux spreads datagrams over the |
| 234 | group; FreeBSD permits the bind and needs SO_REUSEPORT_LB to spread. Darwin | 266 | group. FreeBSD permits the bind and needs SO_REUSEPORT_LB to spread. Darwin |
| 235 | hands every datagram to whichever socket bound last, so a group there starves | 267 | hands every datagram to whichever socket bound last, so a group there starves |
| 236 | all but one reader. | 268 | all but one reader. |
| 237 | 269 | ||
