perf: improve tr_peerMgrGetDesiredAvailable() (#4226)

This commit is contained in:
Charles Kerr
2022-11-22 23:26:10 -06:00
committed by GitHub
parent 40c7f5bceb
commit a8f2b840f7
8 changed files with 236 additions and 75 deletions

View File

@@ -456,3 +456,55 @@ void tr_bitfield::setSpan(size_t begin, size_t end, bool value)
decrementTrueCount(old_count);
}
}
tr_bitfield& tr_bitfield::operator|=(tr_bitfield const& that) noexcept
{
TR_ASSERT(size() == std::size(that));
if (hasAll() || that.hasNone())
{
return *this;
}
if (that.hasAll() || hasNone())
{
*this = that;
return *this;
}
flags_.resize(std::max(std::size(flags_), std::size(that.flags_)));
for (size_t i = 0, n = std::size(flags_); i < n; ++i)
{
flags_[i] |= that.flags_[i];
}
rebuildTrueCount();
return *this;
}
tr_bitfield& tr_bitfield::operator&=(tr_bitfield const& that) noexcept
{
TR_ASSERT(size() == std::size(that));
if (hasNone() || that.hasAll())
{
return *this;
}
if (that.hasNone() || hasAll())
{
*this = that;
return *this;
}
flags_.resize(std::min(std::size(flags_), std::size(that.flags_)));
for (size_t i = 0, n = std::size(flags_); i < n; ++i)
{
flags_[i] &= that.flags_[i];
}
rebuildTrueCount();
return *this;
}

View File

@@ -95,6 +95,24 @@ public:
[[nodiscard]] bool isValid() const;
[[nodiscard]] constexpr auto percent() const noexcept
{
if (hasAll())
{
return 1.0F;
}
if (hasNone() || empty())
{
return 0.0F;
}
return static_cast<float>(count()) / size();
}
tr_bitfield& operator|=(tr_bitfield const& that) noexcept;
tr_bitfield& operator&=(tr_bitfield const& that) noexcept;
private:
[[nodiscard]] size_t countFlags() const noexcept;
[[nodiscard]] size_t countFlags(size_t begin, size_t end) const noexcept;

View File

@@ -187,9 +187,24 @@ public:
virtual bool isTransferringPieces(uint64_t now, tr_direction dir, tr_bytes_per_second_t* setme_bytes_per_second) const = 0;
[[nodiscard]] bool hasPiece(tr_piece_index_t piece) const noexcept
{
return has().test(piece);
}
[[nodiscard]] float percentDone() const noexcept
{
return has().percent();
}
[[nodiscard]] bool isSeed() const noexcept
{
return has().hasAll();
}
[[nodiscard]] virtual std::string readable() const = 0;
[[nodiscard]] virtual bool hasPiece(tr_piece_index_t piece) const noexcept = 0;
[[nodiscard]] virtual tr_bitfield const& has() const noexcept = 0;
[[nodiscard]] virtual tr_bandwidth& bandwidth() noexcept = 0;

View File

@@ -1612,46 +1612,28 @@ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor)
return 0;
}
tr_swarm const* const s = tor->swarm;
if (s == nullptr || !s->is_running)
tr_swarm const* const swarm = tor->swarm;
if (swarm == nullptr || swarm->peerCount() == 0U)
{
return 0;
}
auto const n_peers = s->peerCount();
if (n_peers == 0)
auto available = swarm->peers.front()->has();
for (auto const* const peer : swarm->peers)
{
return 0;
available |= peer->has();
}
for (auto const* const peer : s->peers)
if (available.hasAll())
{
if (peer->atom != nullptr && peer->atom->isSeed())
{
return tor->leftUntilDone();
}
return tor->leftUntilDone();
}
// do it the hard way
auto desired_available = uint64_t{};
auto const n_pieces = tor->pieceCount();
auto have = std::vector<bool>(n_pieces);
for (auto const* const peer : s->peers)
for (tr_piece_index_t i = 0, n = tor->pieceCount(); i < n; ++i)
{
for (tr_piece_index_t j = 0; j < n_pieces; ++j)
{
if (peer->hasPiece(j))
{
have[j] = true;
}
}
}
for (tr_piece_index_t i = 0; i < n_pieces; ++i)
{
if (tor->pieceIsWanted(i) && have.at(i))
if (tor->pieceIsWanted(i) && available.test(i))
{
desired_available += tor->countMissingBytesInPiece(i);
}

View File

@@ -423,24 +423,9 @@ public:
return addr.readable(port);
}
[[nodiscard]] bool isSeed() const noexcept override
[[nodiscard]] tr_bitfield const& has() const noexcept override
{
return have_.hasAll();
}
[[nodiscard]] bool hasPiece(tr_piece_index_t piece) const noexcept override
{
return have_.test(piece);
}
[[nodiscard]] float percentDone() const noexcept override
{
if (!percent_done_)
{
percent_done_ = calculatePercentDone();
}
return *percent_done_;
return have_;
}
void onTorrentGotMetainfo() noexcept override
@@ -450,7 +435,6 @@ public:
void invalidatePercentDone()
{
percent_done_.reset();
updateInterest();
}
@@ -618,24 +602,6 @@ private:
pokeBatchPeriod(ImmediatePriorityIntervalSecs);
}
[[nodiscard]] float calculatePercentDone() const noexcept
{
if (have_.hasAll())
{
return 1.0F;
}
if (have_.hasNone())
{
return 0.0F;
}
auto const true_count = have_.count();
auto const percent_done = torrent->hasMetainfo() ? true_count / static_cast<float>(torrent->pieceCount()) :
true_count / static_cast<float>(std::size(have_) + 1);
return std::clamp(percent_done, 0.0F, 1.0F);
}
[[nodiscard]] bool calculate_active(tr_direction direction) const
{
if (direction == TR_CLIENT_TO_PEER)
@@ -748,8 +714,6 @@ private:
tr_peer_callback const callback_;
void* const callback_data_;
mutable std::optional<float> percent_done_;
// seconds between periodic sendPex() calls
static auto constexpr SendPexInterval = 90s;
};

View File

@@ -60,8 +60,6 @@ public:
virtual void pulse() = 0;
[[nodiscard]] virtual float percentDone() const noexcept = 0;
[[nodiscard]] virtual bool isSeed() const noexcept = 0;
virtual void onTorrentGotMetainfo() = 0;
virtual void on_piece_completed(tr_piece_index_t) = 0;

View File

@@ -154,7 +154,7 @@ private:
void task_request_next_chunk(tr_webseed_task* task);
void onBufferGotData(evbuffer* /*buf*/, evbuffer_cb_info const* info, void* vtask);
class tr_webseed : public tr_peer
class tr_webseed final : public tr_peer
{
public:
tr_webseed(struct tr_torrent* tor, std::string_view url, tr_peer_callback callback_in, void* callback_data_in)
@@ -163,10 +163,12 @@ public:
, base_url{ url }
, callback{ callback_in }
, callback_data{ callback_data_in }
, idle_timer_{ session->timerMaker().create([this]() { on_idle(this); }) }
, have_{ tor->pieceCount() }
, bandwidth_{ &tor->bandwidth_ }
, idle_timer{ session->timerMaker().create([this]() { on_idle(this); }) }
{
idle_timer->startRepeating(IdleTimerInterval);
have_.setHasAll();
idle_timer_->startRepeating(IdleTimerInterval);
}
tr_webseed(tr_webseed&&) = delete;
@@ -238,9 +240,9 @@ public:
return base_url;
}
[[nodiscard]] bool hasPiece(tr_piece_index_t /*piece*/) const noexcept override
[[nodiscard]] tr_bitfield const& has() const noexcept override
{
return true;
return have_;
}
void gotPieceData(uint32_t n_bytes)
@@ -315,9 +317,13 @@ public:
std::set<tr_webseed_task*> tasks;
private:
tr_bandwidth bandwidth_;
std::unique_ptr<libtransmission::Timer> idle_timer;
static auto constexpr IdleTimerInterval = 2s;
std::unique_ptr<libtransmission::Timer> const idle_timer_;
tr_bitfield have_;
tr_bandwidth bandwidth_;
};
/***

View File

@@ -319,3 +319,129 @@ TEST(Bitfield, hasAllNone)
EXPECT_TRUE(!field.hasNone());
}
}
TEST(Bitfield, percent)
{
auto field = tr_bitfield{ 100 };
field.setHasAll();
EXPECT_NEAR(1.0F, field.percent(), 0.01);
field.setHasNone();
EXPECT_NEAR(0.0F, field.percent(), 0.01);
field.setSpan(0, std::size(field) / 2U);
EXPECT_NEAR(0.5F, field.percent(), 0.01);
field.setHasNone();
field.setSpan(0, std::size(field) / 4U);
EXPECT_NEAR(0.25F, field.percent(), 0.01);
}
TEST(Bitfield, bitwise_or)
{
auto a = tr_bitfield{ 100 };
auto b = tr_bitfield{ 100 };
a.setHasAll();
b.setHasNone();
a |= b;
EXPECT_TRUE(a.hasAll());
a.setHasNone();
b.setHasAll();
a |= b;
EXPECT_TRUE(a.hasAll());
a.setHasNone();
b.setHasNone();
a |= b;
EXPECT_TRUE(a.hasNone());
a.setHasNone();
b.setHasNone();
a.setSpan(0, std::size(a) / 2U);
b.setSpan(std::size(a) / 2U, std::size(a));
EXPECT_EQ(0.5, a.percent());
EXPECT_EQ(0.5, b.percent());
a |= b;
EXPECT_EQ(1.0, a.percent());
EXPECT_TRUE(a.hasAll());
a.setHasNone();
b.setHasNone();
for (size_t i = 0; i < std::size(a); ++i)
{
if ((i % 2U) != 0U)
{
a.set(i);
}
else
{
b.set(i);
}
}
EXPECT_NEAR(0.5F, a.percent(), 0.01);
EXPECT_NEAR(0.5F, b.percent(), 0.01);
a |= b;
EXPECT_TRUE(a.hasAll());
}
TEST(Bitfield, bitwise_and)
{
auto a = tr_bitfield{ 100 };
auto b = tr_bitfield{ 100 };
a.setHasAll();
b.setHasNone();
a &= b;
EXPECT_TRUE(a.hasNone());
a.setHasNone();
b.setHasAll();
a &= b;
EXPECT_TRUE(a.hasNone());
a.setHasAll();
b.setHasAll();
a &= b;
EXPECT_TRUE(a.hasAll());
a.setHasNone();
b.setHasNone();
a.setSpan(0, std::size(a) / 2U);
b.setSpan(std::size(a) / 2U, std::size(a));
EXPECT_EQ(0.5, a.percent());
EXPECT_EQ(0.5, b.percent());
a &= b;
EXPECT_TRUE(a.hasNone());
a.setHasNone();
b.setHasNone();
for (size_t i = 0; i < std::size(a); ++i)
{
if ((i % 2U) != 0U)
{
a.set(i);
}
else
{
b.set(i);
}
}
a &= b;
EXPECT_TRUE(a.hasNone());
a.setHasNone();
a.setSpan(0U, std::size(a) / 10U);
b.setHasNone();
b.setSpan(0U, std::size(a) / 20U);
a &= b;
EXPECT_NEAR(0.05F, a.percent(), 0.01);
a.setHasNone();
a.setSpan(0U, std::size(a) / 10U);
b.setHasNone();
b.setSpan(0U, std::size(a) / 20U);
b &= a;
EXPECT_NEAR(0.1F, a.percent(), 0.01);
}