mirror of https://github.com/aria2/aria2
Simplified PieceStatMan and RarestPieceSelector.
Simplified PieceStatMan and RarestPieceSelector, but computation order to select piece index is still O(N) and unchanged. Updating piece stat is improved to O(N) for bitfield update and O(1) for single index update, while old implementation needs O(NlogN) and O(N) respectively.pull/1/head
parent
37016c6587
commit
6ee913b0bc
|
@ -34,6 +34,7 @@
|
|||
/* copyright --> */
|
||||
#include "PieceStatMan.h"
|
||||
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include "SimpleRandomizer.h"
|
||||
|
@ -41,138 +42,80 @@
|
|||
|
||||
namespace aria2 {
|
||||
|
||||
PieceStat::PieceStat(size_t index):order_(0), index_(index), count_(0) {}
|
||||
|
||||
void PieceStat::addCount()
|
||||
{
|
||||
if(count_ < SIZE_MAX) {
|
||||
++count_;
|
||||
}
|
||||
}
|
||||
|
||||
void PieceStat::subCount()
|
||||
{
|
||||
if(count_ > 0) {
|
||||
--count_;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class GenPieceStat {
|
||||
private:
|
||||
size_t index_;
|
||||
public:
|
||||
GenPieceStat():index_(0) {}
|
||||
|
||||
SharedHandle<PieceStat> operator()()
|
||||
{
|
||||
return SharedHandle<PieceStat>(new PieceStat(index_++));
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
PieceStatMan::PieceStatMan(size_t pieceNum, bool randomShuffle):
|
||||
pieceStats_(pieceNum),
|
||||
sortedPieceStatIndexes_(pieceNum)
|
||||
order_(pieceNum),
|
||||
counts_(pieceNum)
|
||||
{
|
||||
std::generate(pieceStats_.begin(), pieceStats_.end(), GenPieceStat());
|
||||
std::vector<SharedHandle<PieceStat> > sortedPieceStats(pieceStats_);
|
||||
for(size_t i = 0; i < pieceNum; ++i) {
|
||||
order_[i] = i;
|
||||
}
|
||||
// we need some randomness in ordering.
|
||||
if(randomShuffle) {
|
||||
std::random_shuffle(sortedPieceStats.begin(), sortedPieceStats.end(),
|
||||
std::random_shuffle(order_.begin(), order_.end(),
|
||||
*(SimpleRandomizer::getInstance().get()));
|
||||
}
|
||||
{
|
||||
size_t order = 0;
|
||||
for(std::vector<SharedHandle<PieceStat> >::const_iterator i =
|
||||
sortedPieceStats.begin(), eoi = sortedPieceStats.end();
|
||||
i != eoi; ++i) {
|
||||
sortedPieceStatIndexes_[order] = (*i)->getIndex();
|
||||
(*i)->setOrder(order++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PieceStatMan::~PieceStatMan() {}
|
||||
|
||||
namespace {
|
||||
class PieceStatRarer {
|
||||
private:
|
||||
const std::vector<SharedHandle<PieceStat> >& pieceStats_;
|
||||
public:
|
||||
PieceStatRarer(const std::vector<SharedHandle<PieceStat> >& ps):
|
||||
pieceStats_(ps) {}
|
||||
|
||||
bool operator()(size_t lhs, size_t rhs) const
|
||||
{
|
||||
return *pieceStats_[lhs] < *pieceStats_[rhs];
|
||||
void inc(int& x)
|
||||
{
|
||||
if(x < std::numeric_limits<int>::max()) {
|
||||
++x;
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void sub(int& x)
|
||||
{
|
||||
if(x > 0) {
|
||||
--x;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void PieceStatMan::addPieceStats(const unsigned char* bitfield,
|
||||
size_t bitfieldLength)
|
||||
{
|
||||
const size_t nbits = pieceStats_.size();
|
||||
assert(nbits <= bitfieldLength*8);
|
||||
for(size_t i = 0; i < nbits; ++i) {
|
||||
for(size_t i = 0, nbits = counts_.size(); i < nbits; ++i) {
|
||||
if(bitfield::test(bitfield, nbits, i)) {
|
||||
pieceStats_[i]->addCount();
|
||||
inc(counts_[i]);
|
||||
}
|
||||
}
|
||||
std::sort(sortedPieceStatIndexes_.begin(), sortedPieceStatIndexes_.end(),
|
||||
PieceStatRarer(pieceStats_));
|
||||
}
|
||||
|
||||
void PieceStatMan::subtractPieceStats(const unsigned char* bitfield,
|
||||
size_t bitfieldLength)
|
||||
{
|
||||
const size_t nbits = pieceStats_.size();
|
||||
assert(nbits <= bitfieldLength*8);
|
||||
for(size_t i = 0; i < nbits; ++i) {
|
||||
for(size_t i = 0, nbits = counts_.size(); i < nbits; ++i) {
|
||||
if(bitfield::test(bitfield, nbits, i)) {
|
||||
pieceStats_[i]->subCount();
|
||||
sub(counts_[i]);
|
||||
}
|
||||
}
|
||||
std::sort(sortedPieceStatIndexes_.begin(), sortedPieceStatIndexes_.end(),
|
||||
PieceStatRarer(pieceStats_));
|
||||
}
|
||||
|
||||
void PieceStatMan::updatePieceStats(const unsigned char* newBitfield,
|
||||
size_t newBitfieldLength,
|
||||
const unsigned char* oldBitfield)
|
||||
{
|
||||
const size_t nbits = pieceStats_.size();
|
||||
assert(nbits <= newBitfieldLength*8);
|
||||
for(size_t i = 0; i < nbits; ++i) {
|
||||
for(size_t i = 0, nbits = counts_.size(); i < nbits; ++i) {
|
||||
bool inNew = bitfield::test(newBitfield, nbits, i);
|
||||
bool inOld = bitfield::test(oldBitfield, nbits, i);
|
||||
if(inNew) {
|
||||
if(!inOld) {
|
||||
pieceStats_[i]->addCount();
|
||||
inc(counts_[i]);
|
||||
}
|
||||
} else if(inOld) {
|
||||
pieceStats_[i]->subCount();
|
||||
sub(counts_[i]);
|
||||
}
|
||||
}
|
||||
std::sort(sortedPieceStatIndexes_.begin(), sortedPieceStatIndexes_.end(),
|
||||
PieceStatRarer(pieceStats_));
|
||||
}
|
||||
|
||||
void PieceStatMan::addPieceStats(size_t index)
|
||||
{
|
||||
std::vector<size_t>::iterator cur =
|
||||
std::lower_bound(sortedPieceStatIndexes_.begin(),
|
||||
sortedPieceStatIndexes_.end(),
|
||||
index, PieceStatRarer(pieceStats_));
|
||||
|
||||
pieceStats_[index]->addCount();
|
||||
|
||||
std::vector<size_t>::iterator to =
|
||||
std::upper_bound(cur+1, sortedPieceStatIndexes_.end(),
|
||||
index, PieceStatRarer(pieceStats_));
|
||||
|
||||
std::rotate(cur, cur+1, to);
|
||||
inc(counts_[index]);
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -43,53 +43,10 @@
|
|||
|
||||
namespace aria2 {
|
||||
|
||||
class PieceStat {
|
||||
private:
|
||||
size_t order_;
|
||||
size_t index_;
|
||||
size_t count_;
|
||||
public:
|
||||
PieceStat(size_t index);
|
||||
|
||||
bool operator<(const PieceStat& pieceStat) const
|
||||
{
|
||||
if(count_ == pieceStat.count_) {
|
||||
return order_ < pieceStat.order_;
|
||||
} else {
|
||||
return count_ < pieceStat.count_;
|
||||
}
|
||||
}
|
||||
|
||||
void addCount();
|
||||
void subCount();
|
||||
|
||||
size_t getOrder() const
|
||||
{
|
||||
return order_;
|
||||
}
|
||||
|
||||
void setOrder(size_t order)
|
||||
{
|
||||
order_ = order;
|
||||
}
|
||||
|
||||
size_t getIndex() const
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
size_t getCount() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PieceStatMan {
|
||||
private:
|
||||
std::vector<SharedHandle<PieceStat> > pieceStats_;
|
||||
|
||||
std::vector<size_t> sortedPieceStatIndexes_;
|
||||
std::vector<size_t> order_;
|
||||
std::vector<int> counts_;
|
||||
public:
|
||||
PieceStatMan(size_t pieceNum, bool randomShuffle);
|
||||
|
||||
|
@ -107,17 +64,15 @@ public:
|
|||
size_t newBitfieldLength,
|
||||
const unsigned char* oldBitfield);
|
||||
|
||||
// Returns piece index in rarest first order.
|
||||
const std::vector<size_t>& getRarerPieceIndexes() const
|
||||
const std::vector<size_t>& getOrder() const
|
||||
{
|
||||
return sortedPieceStatIndexes_;
|
||||
return order_;
|
||||
}
|
||||
|
||||
const std::vector<SharedHandle<PieceStat> >& getPieceStats() const
|
||||
const std::vector<int>& getCounts() const
|
||||
{
|
||||
return pieceStats_;
|
||||
return counts_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -34,47 +34,35 @@
|
|||
/* copyright --> */
|
||||
#include "RarestPieceSelector.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include "PieceStatMan.h"
|
||||
#include "bitfield.h"
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
RarestPieceSelector::RarestPieceSelector
|
||||
(const SharedHandle<PieceStatMan>& pieceStatMan):pieceStatMan_(pieceStatMan) {}
|
||||
|
||||
namespace {
|
||||
class FindRarestPiece
|
||||
{
|
||||
private:
|
||||
const unsigned char* misbitfield_;
|
||||
size_t numbits_;
|
||||
public:
|
||||
FindRarestPiece(const unsigned char* misbitfield, size_t numbits):
|
||||
misbitfield_(misbitfield), numbits_(numbits) {}
|
||||
|
||||
bool operator()(const size_t& index)
|
||||
{
|
||||
assert(index < numbits_);
|
||||
unsigned char mask = (128 >> (index%8));
|
||||
return misbitfield_[index/8]&mask;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool RarestPieceSelector::select
|
||||
(size_t& index, const unsigned char* bitfield, size_t nbits) const
|
||||
{
|
||||
const std::vector<size_t>& pieceIndexes =
|
||||
pieceStatMan_->getRarerPieceIndexes();
|
||||
std::vector<size_t>::const_iterator i =
|
||||
std::find_if(pieceIndexes.begin(), pieceIndexes.end(),
|
||||
FindRarestPiece(bitfield, nbits));
|
||||
if(i == pieceIndexes.end()) {
|
||||
const std::vector<size_t>& order = pieceStatMan_->getOrder();
|
||||
const std::vector<int>& counts = pieceStatMan_->getCounts();
|
||||
int min = std::numeric_limits<int>::max();
|
||||
size_t bestIdx = nbits;
|
||||
for(size_t i = 0; i < nbits; ++i) {
|
||||
size_t idx = order[i];
|
||||
if(bitfield::test(bitfield, nbits, idx) && counts[idx] < min) {
|
||||
min = counts[idx];
|
||||
bestIdx = idx;
|
||||
}
|
||||
}
|
||||
if(bestIdx == nbits) {
|
||||
return false;
|
||||
} else {
|
||||
index = *i;
|
||||
index = bestIdx;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,56 +31,33 @@ void PieceStatManTest::testAddPieceStats_index()
|
|||
PieceStatMan pieceStatMan(10, false);
|
||||
pieceStatMan.addPieceStats(1);
|
||||
{
|
||||
size_t indexes[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 1 };
|
||||
size_t counts[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)10, stats.size());
|
||||
|
||||
int ans[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
const std::vector<size_t>& order(pieceStatMan.getOrder());
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(i, order[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pieceStatMan.addPieceStats(1);
|
||||
|
||||
{
|
||||
size_t indexes[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 1 };
|
||||
size_t counts[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
int ans[] = { 0, 2, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pieceStatMan.addPieceStats(3);
|
||||
pieceStatMan.addPieceStats(9);
|
||||
pieceStatMan.addPieceStats(3);
|
||||
pieceStatMan.addPieceStats(0);
|
||||
|
||||
{
|
||||
size_t indexes[] = { 2, 4, 5, 6, 7, 8, 0, 9, 1, 3 };
|
||||
size_t counts[] = { 0, 0, 0, 0, 0, 0, 1, 1, 2, 2 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
int ans[] = { 1, 2, 0, 2, 0, 0, 0, 0, 0, 1 };
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PieceStatManTest::testAddPieceStats_bitfield()
|
||||
|
@ -89,36 +66,18 @@ void PieceStatManTest::testAddPieceStats_bitfield()
|
|||
const unsigned char bitfield[] = { 0xaa, 0x80 };
|
||||
pieceStatMan.addPieceStats(bitfield, sizeof(bitfield));
|
||||
{
|
||||
size_t indexes[] = { 1, 3, 5, 7, 9, 0, 2, 4, 6, 8 };
|
||||
size_t counts[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)10, stats.size());
|
||||
|
||||
int ans[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pieceStatMan.addPieceStats(bitfield, sizeof(bitfield));
|
||||
|
||||
{
|
||||
size_t indexes[] = { 1, 3, 5, 7, 9, 0, 2, 4, 6, 8 };
|
||||
size_t counts[] = { 0, 0, 0, 0, 0, 2, 2, 2, 2, 2 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)10, stats.size());
|
||||
|
||||
int ans[] = { 2, 0, 2, 0, 2, 0, 2, 0, 2, 0 };
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,13 +85,10 @@ void PieceStatManTest::testAddPieceStats_bitfield()
|
|||
void PieceStatManTest::testUpdatePieceStats()
|
||||
{
|
||||
PieceStatMan pieceStatMan(10, false);
|
||||
|
||||
const unsigned char bitfield[] = { 0xff, 0xc0 };
|
||||
pieceStatMan.addPieceStats(bitfield, sizeof(bitfield));
|
||||
|
||||
const unsigned char oldBitfield[] = { 0xf0, 0x00 };
|
||||
const unsigned char newBitfield[] = { 0x1f, 0x00 };
|
||||
|
||||
pieceStatMan.updatePieceStats(newBitfield, sizeof(newBitfield), oldBitfield);
|
||||
{
|
||||
// idx: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
|
@ -141,19 +97,10 @@ void PieceStatManTest::testUpdatePieceStats()
|
|||
// new: 0, 0, 0, 1, 1, 1, 1, 1, 0, 0
|
||||
// ---------------------------------
|
||||
// res: 0, 0, 0, 1, 2, 2, 2, 2, 1, 1
|
||||
|
||||
size_t indexes[] = { 0, 1, 2, 3, 8, 9, 4, 5, 6, 7 };
|
||||
size_t counts[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 2 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)10, stats.size());
|
||||
|
||||
int ans[] = { 0, 0, 0, 1, 2, 2, 2, 2, 1, 1 };
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,12 +108,9 @@ void PieceStatManTest::testUpdatePieceStats()
|
|||
void PieceStatManTest::testSubtractPieceStats()
|
||||
{
|
||||
PieceStatMan pieceStatMan(10, false);
|
||||
|
||||
const unsigned char bitfield[] = { 0xf0, 0x00 };
|
||||
pieceStatMan.addPieceStats(bitfield, sizeof(bitfield));
|
||||
|
||||
const unsigned char newBitfield[] = { 0x3f, 0x00 };
|
||||
|
||||
pieceStatMan.subtractPieceStats(newBitfield, sizeof(newBitfield));
|
||||
{
|
||||
// idx: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
|
@ -174,19 +118,10 @@ void PieceStatManTest::testSubtractPieceStats()
|
|||
// new: 0, 0, 1, 1, 1, 1, 1, 1, 0, 0
|
||||
// ---------------------------------
|
||||
// res: 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
|
||||
size_t indexes[] = { 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 };
|
||||
size_t counts[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
|
||||
|
||||
const std::vector<size_t>& statsidx(pieceStatMan.getRarerPieceIndexes());
|
||||
const std::vector<SharedHandle<PieceStat> >& stats
|
||||
(pieceStatMan.getPieceStats());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)10, stats.size());
|
||||
|
||||
int ans[] = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
const std::vector<int>& counts(pieceStatMan.getCounts());
|
||||
for(size_t i = 0; i < 10; ++i) {
|
||||
CPPUNIT_ASSERT_EQUAL(indexes[i], statsidx[i]);
|
||||
CPPUNIT_ASSERT_EQUAL(counts[i], stats[statsidx[i]]->getCount());
|
||||
CPPUNIT_ASSERT_EQUAL(ans[i], counts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue