Skip to content

Commit 80d0b6f

Browse files
authored
Merge pull request #121 from artrixdotdev/perf/bitvec-to-arcbitvec
perf: Changed all uses of BitVec to Arc<BitVec>
2 parents f2e6301 + e81ba6d commit 80d0b6f

File tree

4 files changed

+41
-19
lines changed

4 files changed

+41
-19
lines changed

crates/libtortillas/src/peer/actor.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::{collections::HashMap, time::Instant};
1+
use std::{
2+
collections::HashMap,
3+
sync::{Arc, atomic::AtomicU8},
4+
time::Instant,
5+
};
26

37
use anyhow::Context;
48
use bitvec::vec::BitVec;
@@ -156,18 +160,23 @@ impl PeerActor {
156160
#[instrument(skip(self), fields(addr = %self.stream, id = %self.peer.id.unwrap()))]
157161
async fn determine_interest(&mut self) {
158162
let msg = TorrentRequest::Bitfield;
163+
164+
let their_bitfield = self.peer.pieces.clone();
159165
let our_bitfield = match self.supervisor.ask(msg).await.unwrap() {
160166
TorrentResponse::Bitfield(bitfield) => bitfield,
161167
_ => unreachable!("Unexpected response from supervisor"),
162168
};
163-
let their_bitfield = self.peer.pieces.clone();
169+
170+
let is_our_bitfield_empty = our_bitfield.is_empty();
164171

165172
// Find pieces the peer has that we don't have
166-
let peer_has_we_dont = their_bitfield & !our_bitfield.clone();
173+
let their_bitfield = (*their_bitfield).clone();
174+
let our_bitfield = (*our_bitfield).clone();
175+
let peer_has_we_dont = their_bitfield & !our_bitfield;
167176

168177
let has_interesting_pieces = peer_has_we_dont.any();
169178

170-
if our_bitfield.is_empty() || has_interesting_pieces {
179+
if is_our_bitfield_empty || has_interesting_pieces {
171180
self
172181
.stream
173182
.send(PeerMessages::Interested)
@@ -318,7 +327,7 @@ impl Message<PeerMessages> for PeerActor {
318327
}
319328
PeerMessages::Have(piece_index) => {
320329
trace!(piece_index, "Peer has a piece");
321-
self.peer.pieces.set(piece_index as usize, true);
330+
self.peer.pieces.set_aliased(piece_index as usize, true);
322331
}
323332
PeerMessages::Request(index, offset, length) => {
324333
trace!(
@@ -378,7 +387,7 @@ impl Message<PeerMessages> for PeerActor {
378387
#[allow(dead_code)]
379388
pub(crate) enum PeerTell {
380389
NeedPiece(usize, usize, usize),
381-
HaveInfoDict(BitVec<u8>),
390+
HaveInfoDict(Arc<BitVec<AtomicU8>>),
382391
// TODO: Add Have(usize) for notifying the peer that we have a new piece
383392
}
384393

crates/libtortillas/src/peer/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
fmt::{self, Debug, Display},
99
hash::{Hash as InternalHash, Hasher},
1010
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
11+
sync::{Arc, atomic::AtomicU8},
1112
};
1213

1314
pub(crate) use actor::*;
@@ -32,7 +33,7 @@ pub struct Peer {
3233
pub ip: IpAddr,
3334
pub port: u16,
3435
pub state: PeerState,
35-
pub pieces: BitVec<u8>,
36+
pub pieces: Arc<BitVec<AtomicU8>>,
3637
/// The reserved bytes that the peer sent us in their handshake. This
3738
/// indicates what extensions the peer supports.
3839
pub reserved: [u8; 8],
@@ -80,7 +81,7 @@ impl Peer {
8081
ip,
8182
port,
8283
state: PeerState::new(),
83-
pieces: BitVec::EMPTY,
84+
pieces: Arc::new(BitVec::EMPTY),
8485
reserved: [0u8; 8],
8586
peer_supports: PeerSupports::new(),
8687
id: None,

crates/libtortillas/src/protocol/messages.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use std::{
22
collections::HashMap,
33
net::{IpAddr, Ipv4Addr, Ipv6Addr},
4-
sync::Arc,
4+
sync::{
5+
Arc,
6+
atomic::{AtomicU8, Ordering},
7+
},
58
};
69

710
use anyhow::{Error, Result, bail};
@@ -48,7 +51,7 @@ pub enum PeerMessages {
4851
/// Each boolean in the vector corresponds to a piece in the torrent. A
4952
/// `true` value indicates that the peer has the piece, while `false`
5053
/// indicates that it does not. This is typically sent after the handshake.
51-
Bitfield(BitVec<u8>) = 5u8,
54+
Bitfield(Arc<BitVec<AtomicU8>>) = 5u8,
5255
/// A request for a specific block of data from a piece.
5356
/// Length is usually a power of 2.
5457
///
@@ -130,7 +133,9 @@ impl PeerMessages {
130133
PeerMessages::Interested => create_message_with_id(2, &[]),
131134
PeerMessages::NotInterested => create_message_with_id(3, &[]),
132135
PeerMessages::Have(index) => create_message_with_id(4, &index.to_be_bytes()),
133-
PeerMessages::Bitfield(bits) => create_message_with_id(5, bits.as_raw_slice()),
136+
// This code is wildly confusing, but all it does is maps the array of AtomicU8s to an
137+
// vector of u8s
138+
PeerMessages::Bitfield(bits) => create_message_with_id(5, &bits.as_raw_slice().iter().map(|byte| byte.load(Ordering::Acquire)).collect::<Vec<u8>>()),
134139
PeerMessages::Request(index, begin, length) // Code is identical
135140
| PeerMessages::Cancel(index, begin, length) => {
136141
let id = match self {
@@ -253,7 +258,10 @@ impl PeerMessages {
253258
}
254259
5 => {
255260
trace!(bitfield_len = payload.len(), "Received Bitfield message");
256-
Ok(PeerMessages::Bitfield(BitVec::from_slice(&payload)))
261+
262+
let bitvec: BitVec<AtomicU8> = payload.into_iter().map(AtomicU8::new).collect();
263+
264+
Ok(PeerMessages::Bitfield(Arc::new(bitvec)))
257265
}
258266
6 => {
259267
if payload.len() != 12 {

crates/libtortillas/src/torrent/mod.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::{fmt, net::SocketAddr, sync::Arc};
1+
use std::{
2+
fmt,
3+
net::SocketAddr,
4+
sync::{Arc, atomic::AtomicU8},
5+
};
26

37
use bitvec::vec::BitVec;
48
use bytes::Bytes;
@@ -30,7 +34,7 @@ pub struct Torrent {
3034
peers: Arc<DashMap<PeerId, ActorRef<PeerActor>>>,
3135
trackers: Arc<DashMap<Tracker, ActorRef<TrackerActor>>>,
3236

33-
bitfield: BitVec<u8>,
37+
bitfield: Arc<BitVec<AtomicU8>>,
3438
id: PeerId,
3539
info: Option<Info>,
3640
metainfo: MetaInfo,
@@ -252,7 +256,7 @@ actor_request_response!(
252256

253257
/// Bitfield of the torrent
254258
Bitfield
255-
Bitfield(BitVec<u8>),
259+
Bitfield(Arc<BitVec<AtomicU8>>),
256260

257261
/// Current peers of the torrent
258262
CurrentPeers
@@ -318,11 +322,11 @@ impl Actor for Torrent {
318322
if info.is_none() {
319323
debug!("No info dict found in metainfo, you're probably using a magnet uri");
320324
}
321-
let bitfield: BitVec<u8> = if let Some(info) = &info {
325+
let bitfield: Arc<BitVec<AtomicU8>> = if let Some(info) = &info {
322326
debug!("Using bitfield length {}", info.piece_count());
323-
BitVec::with_capacity(info.piece_count())
327+
Arc::new(BitVec::with_capacity(info.piece_count()))
324328
} else {
325-
BitVec::EMPTY
329+
Arc::new(BitVec::EMPTY)
326330
};
327331

328332
Ok(Self {
@@ -373,7 +377,7 @@ impl Message<TorrentMessage> for Torrent {
373377
info!("Received valid info dict, starting torrent process...");
374378
let info: Info =
375379
serde_bencode::from_bytes(&bytes).expect("Failed to parse info dict");
376-
self.bitfield.resize(info.piece_count(), false);
380+
self.bitfield = Arc::new(BitVec::with_capacity(info.piece_count()));
377381
self.info = Some(info);
378382
self
379383
.broadcast_to_peers(PeerTell::HaveInfoDict(self.bitfield.clone()))

0 commit comments

Comments
 (0)