The problem with this implementation is that fast uploading sources will sooner run out of blocks to download then slow downloading blocks (because fast uploading sources request blocks more often).
The result is that at the end of the download emule is stuck with all the slow sources that only give a few bytes / secs. An experienced user knows that if emule says the download still needs 30 seconds that it can take more then 10 minutes to complete if the source only gives 100b/s.
An easy solution to this problem would be to drop the slowest source from from the download list whenever a source needs more blocks. This means emule would always stick with the fastest sources instead of the slowest sources. The code changes are very simple. I've implemented it in my mod and it easily save 10-20 minutes at the end of any file. Previously an mp3 with 400 sources easily took 20 minutes to download, no I can finish any of them within 5 minutes. It also helps at then end of other files, whenever you end with downloads from multiple sources.
These are the changes:
void CUpDownClient::CreateBlockRequests(int iMaxBlocks) { ASSERT( iMaxBlocks >= 1 /*&& iMaxBlocks <= 3*/ ); if (m_DownloadBlocks_list.IsEmpty()) { //dazzle: prevent a sign error from occurring uint16 count, countinit; if (iMaxBlocks - m_PendingBlocks_list.GetCount()> 0) countinit = iMaxBlocks - m_PendingBlocks_list.GetCount(); else countinit = 0; count = countinit; //end dazzle Requested_Block_Struct** toadd = new Requested_Block_Struct*[count]; if (reqfile->GetNextRequestedBlock(this,toadd,&count)){ for (int i = 0; i < count; i++) m_DownloadBlocks_list.AddTail(toadd[i]); } //dazzle: if no blocks available, try to drop the slowest source, then try again else { if (reqfile->DropSlowestSource(this)) { count = countinit; if (reqfile->GetNextRequestedBlock(this,toadd,&count)){ for (int i = 0; i < count; i++) m_DownloadBlocks_list.AddTail(toadd[i]); } } } //end dazzle delete[] toadd; } while (m_PendingBlocks_list.GetCount() < iMaxBlocks && !m_DownloadBlocks_list.IsEmpty()) { Pending_Block_Struct* pblock = new Pending_Block_Struct; pblock->block = m_DownloadBlocks_list.RemoveHead(); m_PendingBlocks_list.AddTail(pblock); } } //dazzle: This function is used to increase endgame speed. Called whenever no blocks are available to download for a speedy source. // this function drops the slowest source so the speedy source can download his blocks. boolean CPartFile::DropSlowestSource(CUpDownClient* calling_source) { uint32 lowestspeed = 2000000000; CUpDownClient* slowest_source= NULL; for (POSITION pos = m_downloadingSourceList.GetHeadPosition(); pos != NULL;) { CUpDownClient* cur_src = srclist.GetNext(pos); if (cur_src->GetDownloadDatarate() < lowestspeed) { lowestspeed = cur_src->GetDownloadDatarate(); slowest_source = cur_src; } } if (m_downloadingSourceList.GetCount() > 1 && slowest_source && slowest_source!=calling_source ){ // only remove source if it isn't the last one ASSERT( slowest_source->socket != NULL ); if (slowest_source->socket != NULL) { ASSERT( !slowest_source->socket->IsRawDataMode() ); if (!slowest_source->socket->IsRawDataMode()) slowest_source->SendCancelTransfer(); } slowest_source->SetDownloadState(DS_ONQUEUE, _T("endgame: replacing client for a faster one")); return true; } else return false; } //end dazzle