mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 20:35:36 +00:00
perf: improve tr_peerMgrGetDesiredAvailable() (#4226)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
/***
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user