piece picker optimization. O(log n), lookup of downloading pieces instead of O(n)

This commit is contained in:
Arvid Norberg 2011-08-15 04:16:43 +00:00
parent b9be2fe94b
commit 25ad24ec68
2 changed files with 75 additions and 74 deletions

View File

@ -353,6 +353,14 @@ namespace libtorrent
int index; int index;
}; };
struct compare_index
{
bool operator()(const downloading_piece& p, int piece) const
{ return p.index < piece; }
bool operator()(int piece, const downloading_piece& p) const
{ return piece < p.index; }
};
int blocks_in_last_piece() const int blocks_in_last_piece() const
{ return m_blocks_in_last_piece; } { return m_blocks_in_last_piece; }
@ -483,11 +491,14 @@ namespace libtorrent
// shuffles the given piece inside it's priority range // shuffles the given piece inside it's priority range
void shuffle(int priority, int elem_index); void shuffle(int priority, int elem_index);
void sort_piece(std::vector<downloading_piece>::iterator dp); // void sort_piece(std::vector<downloading_piece>::iterator dp);
downloading_piece& add_download_piece(); downloading_piece& add_download_piece(int index);
void erase_download_piece(std::vector<downloading_piece>::iterator i); void erase_download_piece(std::vector<downloading_piece>::iterator i);
std::vector<downloading_piece>::const_iterator find_dl_piece(int index) const;
std::vector<downloading_piece>::iterator find_dl_piece(int index);
// some compilers (e.g. gcc 2.95, does not inherit access // some compilers (e.g. gcc 2.95, does not inherit access
// privileges to nested classes) // privileges to nested classes)
public: public:
@ -520,7 +531,8 @@ namespace libtorrent
// each piece that's currently being downloaded // each piece that's currently being downloaded
// has an entry in this list with block allocations. // has an entry in this list with block allocations.
// i.e. it says wich parts of the piece that // i.e. it says wich parts of the piece that
// is being downloaded // is being downloaded. This list is ordered
// by piece index to make lookups efficient
std::vector<downloading_piece> m_downloads; std::vector<downloading_piece> m_downloads;
// this holds the information of the // this holds the information of the

View File

@ -137,9 +137,7 @@ namespace libtorrent
if (m_piece_map[index].downloading) if (m_piece_map[index].downloading)
{ {
std::vector<downloading_piece>::const_iterator piece = std::find_if( std::vector<downloading_piece>::const_iterator piece = find_dl_piece(index);
m_downloads.begin(), m_downloads.end()
, boost::bind(&downloading_piece::index, _1) == index);
TORRENT_ASSERT(piece != m_downloads.end()); TORRENT_ASSERT(piece != m_downloads.end());
st = *piece; st = *piece;
st.info = 0; st.info = 0;
@ -157,7 +155,7 @@ namespace libtorrent
st.finished = 0; st.finished = 0;
} }
piece_picker::downloading_piece& piece_picker::add_download_piece() piece_picker::downloading_piece& piece_picker::add_download_piece(int piece)
{ {
int num_downloads = m_downloads.size(); int num_downloads = m_downloads.size();
int block_index = num_downloads * m_blocks_per_piece; int block_index = num_downloads * m_blocks_per_piece;
@ -173,8 +171,12 @@ namespace libtorrent
m_downloads[i].info = &m_block_info[m_downloads[i].info - base]; m_downloads[i].info = &m_block_info[m_downloads[i].info - base];
} }
} }
m_downloads.push_back(downloading_piece()); std::vector<downloading_piece>::iterator i = std::lower_bound(m_downloads.begin()
downloading_piece& ret = m_downloads.back(); , m_downloads.end(), piece, compare_index());
TORRENT_ASSERT(i == m_downloads.end() || i->index != piece);
i = m_downloads.insert(i, downloading_piece());
downloading_piece& ret = *i;
ret.index = piece;
ret.info = &m_block_info[block_index]; ret.info = &m_block_info[block_index];
for (int i = 0; i < m_blocks_per_piece; ++i) for (int i = 0; i < m_blocks_per_piece; ++i)
{ {
@ -764,7 +766,7 @@ namespace libtorrent
p2.index = temp; p2.index = temp;
std::swap(m_pieces[other_index], m_pieces[elem_index]); std::swap(m_pieces[other_index], m_pieces[elem_index]);
} }
/*
void piece_picker::sort_piece(std::vector<downloading_piece>::iterator dp) void piece_picker::sort_piece(std::vector<downloading_piece>::iterator dp)
{ {
TORRENT_ASSERT(m_piece_map[dp->index].downloading); TORRENT_ASSERT(m_piece_map[dp->index].downloading);
@ -793,7 +795,7 @@ namespace libtorrent
if (j == m_downloads.end() - 1) return; if (j == m_downloads.end() - 1) return;
} }
} }
*/
void piece_picker::restore_piece(int index) void piece_picker::restore_piece(int index)
{ {
TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_PIECE_PICKER_INVARIANT_CHECK;
@ -803,9 +805,7 @@ namespace libtorrent
TORRENT_ASSERT(m_piece_map[index].downloading == 1); TORRENT_ASSERT(m_piece_map[index].downloading == 1);
std::vector<downloading_piece>::iterator i std::vector<downloading_piece>::iterator i = find_dl_piece(index);
= std::find_if(m_downloads.begin(), m_downloads.end()
, has_index(index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
#ifdef TORRENT_DEBUG #ifdef TORRENT_DEBUG
@ -1090,15 +1090,12 @@ namespace libtorrent
if (p.downloading) if (p.downloading)
{ {
std::vector<downloading_piece>::iterator i std::vector<downloading_piece>::iterator i
= std::find_if(m_downloads.begin() = find_dl_piece(index);
, m_downloads.end()
, has_index(index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
erase_download_piece(i); erase_download_piece(i);
} }
TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() TORRENT_ASSERT(find_dl_piece(index) == m_downloads.end());
, has_index(index)) == m_downloads.end());
if (p.have()) return; if (p.have()) return;
@ -1576,8 +1573,7 @@ namespace libtorrent
, num_blocks); , num_blocks);
if (num_blocks <= 0) return; if (num_blocks <= 0) return;
num_blocks = append_blocks(interesting_blocks, backup_blocks2 num_blocks = append_blocks(interesting_blocks, backup_blocks2, num_blocks);
, num_blocks);
if (num_blocks <= 0) return; if (num_blocks <= 0) return;
// don't double-pick anything if the peer is on parole // don't double-pick anything if the peer is on parole
@ -1659,8 +1655,7 @@ namespace libtorrent
if (piece_priority(i) == 0) continue; if (piece_priority(i) == 0) continue;
if (have_piece(i)) continue; if (have_piece(i)) continue;
std::vector<downloading_piece>::const_iterator k std::vector<downloading_piece>::const_iterator k = find_dl_piece(i);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(i));
TORRENT_ASSERT(k != m_downloads.end()); TORRENT_ASSERT(k != m_downloads.end());
if (k == m_downloads.end()) continue; if (k == m_downloads.end()) continue;
@ -1771,8 +1766,7 @@ namespace libtorrent
// looked through the downloading pieces // looked through the downloading pieces
if (options & prioritize_partials) return num_blocks; if (options & prioritize_partials) return num_blocks;
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(piece);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(piece));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
// std::cout << "add_blocks_downloading(" << piece << ")" << std::endl; // std::cout << "add_blocks_downloading(" << piece << ")" << std::endl;
@ -1829,7 +1823,7 @@ namespace libtorrent
// if all blocks have been requested (and we don't need any backup // if all blocks have been requested (and we don't need any backup
// blocks), we might as well return immediately // blocks), we might as well return immediately
if (int(backup_blocks2.size()) >= num_blocks if (int(backup_blocks2.size()) >= 1
&& int(backup_blocks.size()) >= num_blocks && int(backup_blocks.size()) >= num_blocks
&& dp.requested + dp.writing + dp.finished == num_blocks_in_piece) && dp.requested + dp.writing + dp.finished == num_blocks_in_piece)
return num_blocks; return num_blocks;
@ -1850,7 +1844,7 @@ namespace libtorrent
// downloading from this piece, add it as backups // downloading from this piece, add it as backups
if (prefer_whole_pieces > 0 && !exclusive_active) if (prefer_whole_pieces > 0 && !exclusive_active)
{ {
if (int(backup_blocks2.size()) >= num_blocks) if (int(backup_blocks2.size()) >= 1)
return num_blocks; return num_blocks;
for (int j = 0; j < num_blocks_in_piece; ++j) for (int j = 0; j < num_blocks_in_piece; ++j)
@ -1887,7 +1881,7 @@ namespace libtorrent
else else
{ {
// don't pick too many back-up blocks // don't pick too many back-up blocks
if (int(backup_blocks2.size()) >= num_blocks) return num_blocks; if (int(backup_blocks2.size()) >= 1) return num_blocks;
backup_blocks2.push_back(piece_block(dp.index, j)); backup_blocks2.push_back(piece_block(dp.index, j));
} }
continue; continue;
@ -1947,12 +1941,10 @@ namespace libtorrent
if (m_piece_map[index].downloading == 0) if (m_piece_map[index].downloading == 0)
{ {
TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() TORRENT_ASSERT(find_dl_piece(index) == m_downloads.end());
, has_index(index)) == m_downloads.end());
return false; return false;
} }
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
TORRENT_ASSERT((int)i->finished <= m_blocks_per_piece); TORRENT_ASSERT((int)i->finished <= m_blocks_per_piece);
int max_blocks = blocks_in_piece(index); int max_blocks = blocks_in_piece(index);
@ -1970,6 +1962,24 @@ namespace libtorrent
return true; return true;
} }
std::vector<piece_picker::downloading_piece>::iterator piece_picker::find_dl_piece(int index)
{
// return std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
std::vector<piece_picker::downloading_piece>::iterator i = std::lower_bound(
m_downloads.begin(), m_downloads.end(), index, compare_index());
if (i->index == index) return i;
return m_downloads.end();
}
std::vector<piece_picker::downloading_piece>::const_iterator piece_picker::find_dl_piece(int index) const
{
// return std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
std::vector<piece_picker::downloading_piece>::const_iterator i = std::lower_bound(
m_downloads.begin(), m_downloads.end(), index, compare_index());
if (i->index == index) return i;
return m_downloads.end();
}
bool piece_picker::is_requested(piece_block block) const bool piece_picker::is_requested(piece_block block) const
{ {
TORRENT_ASSERT(block.piece_index >= 0); TORRENT_ASSERT(block.piece_index >= 0);
@ -1977,11 +1987,7 @@ namespace libtorrent
TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(block.piece_index < m_piece_map.size());
if (m_piece_map[block.piece_index].downloading == 0) return false; if (m_piece_map[block.piece_index].downloading == 0) return false;
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(block.piece_index);
= std::find_if(
m_downloads.begin()
, m_downloads.end()
, has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
return i->info[block.block_index].state == block_info::state_requested; return i->info[block.block_index].state == block_info::state_requested;
@ -1995,8 +2001,7 @@ namespace libtorrent
if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true;
if (m_piece_map[block.piece_index].downloading == 0) return false; if (m_piece_map[block.piece_index].downloading == 0) return false;
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
return i->info[block.block_index].state == block_info::state_finished return i->info[block.block_index].state == block_info::state_finished
|| i->info[block.block_index].state == block_info::state_writing; || i->info[block.block_index].state == block_info::state_writing;
@ -2010,8 +2015,7 @@ namespace libtorrent
if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true;
if (m_piece_map[block.piece_index].downloading == 0) return false; if (m_piece_map[block.piece_index].downloading == 0) return false;
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
return i->info[block.block_index].state == block_info::state_finished; return i->info[block.block_index].state == block_info::state_finished;
} }
@ -2038,8 +2042,7 @@ namespace libtorrent
p.downloading = 1; p.downloading = 1;
if (prio >= 0 && !m_dirty) update(prio, p.index); if (prio >= 0 && !m_dirty) update(prio, p.index);
downloading_piece& dp = add_download_piece(); downloading_piece& dp = add_download_piece(block.piece_index);
dp.index = block.piece_index;
dp.state = state; dp.state = state;
block_info& info = dp.info[block.block_index]; block_info& info = dp.info[block.block_index];
info.state = block_info::state_requested; info.state = block_info::state_requested;
@ -2052,8 +2055,7 @@ namespace libtorrent
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_PIECE_PICKER_INVARIANT_CHECK;
#endif #endif
std::vector<downloading_piece>::iterator i std::vector<downloading_piece>::iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
block_info& info = i->info[block.block_index]; block_info& info = i->info[block.block_index];
if (info.state == block_info::state_writing if (info.state == block_info::state_writing
@ -2084,8 +2086,7 @@ namespace libtorrent
piece_pos const& p = m_piece_map[block.piece_index]; piece_pos const& p = m_piece_map[block.piece_index];
if (!p.downloading) return 0; if (!p.downloading) return 0;
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
block_info const& info = i->info[block.block_index]; block_info const& info = i->info[block.block_index];
@ -2132,20 +2133,18 @@ namespace libtorrent
// the piece priority was set to 0 // the piece priority was set to 0
if (prio >= 0 && !m_dirty) update(prio, p.index); if (prio >= 0 && !m_dirty) update(prio, p.index);
downloading_piece& dp = add_download_piece(); downloading_piece& dp = add_download_piece(block.piece_index);
dp.index = block.piece_index;
dp.state = none; dp.state = none;
block_info& info = dp.info[block.block_index]; block_info& info = dp.info[block.block_index];
info.state = block_info::state_writing; info.state = block_info::state_writing;
info.peer = peer; info.peer = peer;
info.num_peers = 0; info.num_peers = 0;
dp.writing = 1; dp.writing = 1;
sort_piece(m_downloads.end()-1); // sort_piece(m_downloads.end()-1);
} }
else else
{ {
std::vector<downloading_piece>::iterator i std::vector<downloading_piece>::iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
block_info& info = i->info[block.block_index]; block_info& info = i->info[block.block_index];
@ -2169,7 +2168,7 @@ namespace libtorrent
// remove the fast/slow state from it // remove the fast/slow state from it
i->state = none; i->state = none;
} }
sort_piece(i); // sort_piece(i);
} }
return true; return true;
} }
@ -2178,8 +2177,7 @@ namespace libtorrent
{ {
TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_PIECE_PICKER_INVARIANT_CHECK;
std::vector<downloading_piece>::iterator i std::vector<downloading_piece>::iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
if (i == m_downloads.end()) return; if (i == m_downloads.end()) return;
@ -2211,7 +2209,7 @@ namespace libtorrent
} }
else else
{ {
sort_piece(i); // sort_piece(i);
} }
} }
@ -2240,8 +2238,7 @@ namespace libtorrent
p.downloading = 1; p.downloading = 1;
if (prio >= 0 && !m_dirty) update(prio, p.index); if (prio >= 0 && !m_dirty) update(prio, p.index);
downloading_piece& dp = add_download_piece(); downloading_piece& dp = add_download_piece(block.piece_index);
dp.index = block.piece_index;
dp.state = none; dp.state = none;
block_info& info = dp.info[block.block_index]; block_info& info = dp.info[block.block_index];
info.peer = peer; info.peer = peer;
@ -2250,7 +2247,7 @@ namespace libtorrent
if (info.state != block_info::state_finished) if (info.state != block_info::state_finished)
{ {
++dp.finished; ++dp.finished;
sort_piece(m_downloads.end() - 1); // sort_piece(m_downloads.end() - 1);
} }
info.state = block_info::state_finished; info.state = block_info::state_finished;
} }
@ -2260,8 +2257,7 @@ namespace libtorrent
TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_PIECE_PICKER_INVARIANT_CHECK;
#endif #endif
std::vector<downloading_piece>::iterator i std::vector<downloading_piece>::iterator i = find_dl_piece(block.piece_index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
block_info& info = i->info[block.block_index]; block_info& info = i->info[block.block_index];
@ -2282,7 +2278,7 @@ namespace libtorrent
{ {
TORRENT_ASSERT(info.state == block_info::state_none); TORRENT_ASSERT(info.state == block_info::state_none);
info.state = block_info::state_finished; info.state = block_info::state_finished;
sort_piece(i); // sort_piece(i);
} }
} }
} }
@ -2290,8 +2286,7 @@ namespace libtorrent
void piece_picker::get_downloaders(std::vector<void*>& d, int index) const void piece_picker::get_downloaders(std::vector<void*>& d, int index) const
{ {
TORRENT_ASSERT(index >= 0 && index <= (int)m_piece_map.size()); TORRENT_ASSERT(index >= 0 && index <= (int)m_piece_map.size());
std::vector<downloading_piece>::const_iterator i std::vector<downloading_piece>::const_iterator i = find_dl_piece(index);
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
d.clear(); d.clear();
@ -2303,10 +2298,7 @@ namespace libtorrent
void* piece_picker::get_downloader(piece_block block) const void* piece_picker::get_downloader(piece_block block) const
{ {
std::vector<downloading_piece>::const_iterator i = std::find_if( std::vector<downloading_piece>::const_iterator i = find_dl_piece(block.piece_index);
m_downloads.begin()
, m_downloads.end()
, has_index(block.piece_index));
if (i == m_downloads.end()) return 0; if (i == m_downloads.end()) return 0;
@ -2333,13 +2325,11 @@ namespace libtorrent
if (m_piece_map[block.piece_index].downloading == 0) if (m_piece_map[block.piece_index].downloading == 0)
{ {
TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() TORRENT_ASSERT(find_dl_piece(block.piece_index) == m_downloads.end());
, has_index(block.piece_index)) == m_downloads.end());
return; return;
} }
std::vector<downloading_piece>::iterator i = std::find_if(m_downloads.begin() std::vector<downloading_piece>::iterator i = find_dl_piece(block.piece_index);
, m_downloads.end(), has_index(block.piece_index));
TORRENT_ASSERT(i != m_downloads.end()); TORRENT_ASSERT(i != m_downloads.end());
block_info& info = i->info[block.block_index]; block_info& info = i->info[block.block_index];
@ -2386,8 +2376,7 @@ namespace libtorrent
else if (prev_prio >= 0) update(prev_prio, p.index); else if (prev_prio >= 0) update(prev_prio, p.index);
} }
TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() TORRENT_ASSERT(find_dl_piece(block.piece_index) == m_downloads.end());
, has_index(block.piece_index)) == m_downloads.end());
} }
else if (i->requested == 0) else if (i->requested == 0)
{ {