Context
Audit of the current Rust actor model and lightweight profiling on Fri May 22 2026. No memory-related changes were made.
Measurements
cargo test --workspace: passed, 17 unit/integration tests and 21 doctests passed; 7 runtime tests and 8 doctests ignored.
cargo nextest run --workspace: passed, 17 tests in about 0.027s after build.
cargo clippy --workspace --all-targets -- -D warnings: passed.
hyperfine --warmup 2 'cargo test --workspace --quiet': mean 1.402s ± 0.047s.
perf stat ... cargo test --workspace --quiet: 1.43s elapsed, 161,042 page faults, 11.19B instructions, 7.52B cycles.
- Sampled peak RSS while running
cargo test --workspace --quiet: about 281,636 KiB. This includes Cargo, rustdoc, and test-process overhead, not only the library runtime.
- Release artifacts:
target/release/tortillas: 872K
target/release/liblibtortillas.rlib: 14M
Observations
- Torrent and peer actor boundaries are a good fit for isolating per-peer protocol state, torrent scheduling state, and tracker communication.
- Several request paths clone potentially large state:
TorrentRequest::Bitfield returns Arc::new(self.bitfield.clone())
TorrentRequest::HasInfoDict clones Info
TorrentRequest::Export clones metainfo, info, bitfield, and block maps
PeerActor::determine_interest clones both peer and torrent bitfields before computing interest. This can become expensive with many pieces and peers.
- Piece validation currently reads a full piece into memory after validating it, then sends that
Bytes to the piece manager. This is simple but creates a per-completed-piece allocation equal to piece size.
- Outgoing
PeerMessages::Piece serialization builds a new payload buffer containing headers plus the block bytes. That copies block payloads on upload.
PieceStoreActor serializes all disk writes and validations through one actor, which simplifies safety but can become a throughput bottleneck under many peers and torrents.
- Peer pending block requests use
Arc<DashSet<_>> even though access appears actor-local. That likely adds unnecessary synchronization and allocation overhead.
- Default torrent mailbox is bounded at 64, but peer actor mailbox sizing and backpressure should be reviewed for heavy piece traffic.
reqwest with default features plus tokio = full likely pulls in more dependencies and runtime surface than needed. This affects binary and build size more than hot-path memory, but is worth trimming before release.
Follow-up Work
- Add deterministic microbenchmarks for:
- message encode/decode
- scheduler request selection
- bitfield interest checks
- piece validation/write flow
- Add an end-to-end local swarm benchmark that avoids public trackers and records throughput, allocations/RSS, peer count, and block latency.
- Profile a real download and seeding scenario with heap tools available in CI or dev docs, such as heaptrack or Valgrind Massif where available.
- Replace bitfield and info clones on hot actor requests with shared immutable state or borrowed computations where possible.
- Consider changing
PeerActor::pending_block_requests from Arc<DashSet<_>> to an actor-local HashSet if no cross-thread access is required.
- Investigate streaming or zero-copy paths for piece validation and upload serialization.
- Review mailbox capacities and backpressure policy for piece-heavy workloads.
- Trim dependency features, especially
tokio = full and reqwest defaults, once runtime requirements are clear.
Context
Audit of the current Rust actor model and lightweight profiling on Fri May 22 2026. No memory-related changes were made.
Measurements
cargo test --workspace: passed, 17 unit/integration tests and 21 doctests passed; 7 runtime tests and 8 doctests ignored.cargo nextest run --workspace: passed, 17 tests in about 0.027s after build.cargo clippy --workspace --all-targets -- -D warnings: passed.hyperfine --warmup 2 'cargo test --workspace --quiet': mean 1.402s ± 0.047s.perf stat ... cargo test --workspace --quiet: 1.43s elapsed, 161,042 page faults, 11.19B instructions, 7.52B cycles.cargo test --workspace --quiet: about 281,636 KiB. This includes Cargo, rustdoc, and test-process overhead, not only the library runtime.target/release/tortillas: 872Ktarget/release/liblibtortillas.rlib: 14MObservations
TorrentRequest::BitfieldreturnsArc::new(self.bitfield.clone())TorrentRequest::HasInfoDictclonesInfoTorrentRequest::Exportclones metainfo, info, bitfield, and block mapsPeerActor::determine_interestclones both peer and torrent bitfields before computing interest. This can become expensive with many pieces and peers.Bytesto the piece manager. This is simple but creates a per-completed-piece allocation equal to piece size.PeerMessages::Pieceserialization builds a new payload buffer containing headers plus the block bytes. That copies block payloads on upload.PieceStoreActorserializes all disk writes and validations through one actor, which simplifies safety but can become a throughput bottleneck under many peers and torrents.Arc<DashSet<_>>even though access appears actor-local. That likely adds unnecessary synchronization and allocation overhead.reqwestwith default features plustokio = fulllikely pulls in more dependencies and runtime surface than needed. This affects binary and build size more than hot-path memory, but is worth trimming before release.Follow-up Work
PeerActor::pending_block_requestsfromArc<DashSet<_>>to an actor-localHashSetif no cross-thread access is required.tokio = fullandreqwestdefaults, once runtime requirements are clear.