diff --git a/crates/rib/src/table/mod.rs b/crates/rib/src/table/mod.rs index 0b3d5aa..4fc5ad6 100644 --- a/crates/rib/src/table/mod.rs +++ b/crates/rib/src/table/mod.rs @@ -193,13 +193,16 @@ impl Row { .position(|(index, path)| path.peer() == peer && self.path_id(index) == path_id) } - fn push(&mut self, path: Path, path_id: u32) { + /// Adds a route. `peers` is how many peers the table has: a prefix + /// that one of them announces, the others mostly announce too, so a + /// row is given room for that many at its first growth. + fn push(&mut self, path: Path, path_id: u32, peers: usize) { if path_id != 0 && self.path_ids.is_empty() { self.path_ids = vec![0; self.paths.len()]; } - push_tight(&mut self.paths, path); + push_tight(&mut self.paths, path, peers); if !self.path_ids.is_empty() { - push_tight(&mut self.path_ids, path_id); + push_tight(&mut self.path_ids, path_id, peers); } } @@ -855,12 +858,25 @@ fn spread(hash: u64, count: usize) -> usize { ((stirred * count as u64) >> 32) as usize } +/// The most room a row is given at its first growth, however many peers +/// there are: a route server's clients do not all announce every prefix. +const FIRST_ROOM: usize = 8; + /// Adds to a vector without the doubling that `push` does: a row of a few -/// hundred routes should not hold room for as many again. Grows by a -/// quarter, which keeps adding cheap on average. -fn push_tight(vec: &mut Vec, value: T) { +/// hundred routes should not hold room for as many again. The first +/// growth is to `expected` (at most [`FIRST_ROOM`]), since with several +/// full tables nearly every row gets that many routes, and growing a row +/// one route at a time was a twelfth of what taking in four tables cost. +/// After that it grows by a quarter, which keeps adding cheap on average. +fn push_tight(vec: &mut Vec, value: T, expected: usize) { if vec.len() == vec.capacity() { - vec.reserve_exact((vec.len() / 4).max(1)); + let first = expected.min(FIRST_ROOM); + let more = if vec.capacity() < first { + first - vec.len() + } else { + (vec.len() / 4).max(1) + }; + vec.reserve_exact(more); } vec.push(value); } @@ -902,7 +918,7 @@ impl Shard { let (outcome, best_changed) = match row.position(peer, path_id) { None => { - row.push(path, path_id); + row.push(path, path_id, self.peers.len()); self.routes += 1; let outcome = Outcome { added: 1, diff --git a/crates/rib/src/table/tests.rs b/crates/rib/src/table/tests.rs index 54500db..fa8dbe2 100644 --- a/crates/rib/src/table/tests.rs +++ b/crates/rib/src/table/tests.rs @@ -957,13 +957,13 @@ fn a_row_can_hold_more_routes_than_a_sixteen_bit_index_counts() { let mut scratch = select::Scratch::default(); let count = usize::from(u16::MAX) + 10; for path_id in 2..=count as u32 { - row.push(path(&worse), path_id); + row.push(path(&worse), path_id, peers.len()); } // Equal routes from one peer: the lowest identifier is the best. assert!(!select::after_add(&mut row, &peers, &mut scratch)); assert_eq!(row.best(), 0); - row.push(path(&better), count as u32 + 1); + row.push(path(&better), count as u32 + 1, peers.len()); assert!(select::after_add(&mut row, &peers, &mut scratch)); assert_eq!(row.best(), count); assert_eq!(row.route(row.best()).path_id, count as u32 + 1); diff --git a/docs/content/docs/development/testing.md b/docs/content/docs/development/testing.md index 67f2967..f13f761 100644 --- a/docs/content/docs/development/testing.md +++ b/docs/content/docs/development/testing.md @@ -214,6 +214,7 @@ environment variables that control the run. | `HOLD_SECS` | How long the sessions stay open after the table is sent | 30 | | `FREQ` | The sampling frequency in hertz | 1999 | | `OUT` | The directory for the results | `~/profile` | +| `JEMALLOC_CONF` | jemalloc options for the daemon (its `MALLOC_CONF`), for example `thp:always` | none | When run through `scripts/linux-vm.sh`, pass the variables with `env`, because the virtual machine does not receive the host's environment: diff --git a/scripts/profile/full-table.sh b/scripts/profile/full-table.sh index b776584..1318454 100755 --- a/scripts/profile/full-table.sh +++ b/scripts/profile/full-table.sh @@ -104,7 +104,9 @@ if [ "$kernel" = 1 ]; then echo 'kernel { stale_seconds: 0 }' >> "$out/daemon.textpb" fi -run "$daemon_bin" "$out/daemon.textpb" --level warn > "$out/daemon.log" 2>&1 & +# `JEMALLOC_CONF`, if set, is jemalloc's MALLOC_CONF for the daemon: +# "thp:always", say. The symbols are prefixed, hence the variable's name. +run env _RJEM_MALLOC_CONF="${JEMALLOC_CONF:-}" "$daemon_bin" "$out/daemon.textpb" --level warn > "$out/daemon.log" 2>&1 & for _ in $(seq 100); do run curl -sf http://127.0.0.1:8080/api/status > /dev/null 2>&1 && break sleep 0.1