2010-07-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

FeedbackURISelector now tries to chooses URI which is not used in
	aria2 globally. If it is possible, it may return used URI.
	* src/AdaptiveURISelector.cc
	* src/AdaptiveURISelector.h
	* src/CreateRequestCommand.cc
	* src/FeedbackURISelector.cc
	* src/FeedbackURISelector.h
	* src/FileEntry.cc
	* src/FileEntry.h
	* src/InOrderURISelector.cc
	* src/InOrderURISelector.h
	* src/RequestGroupMan.cc
	* src/RequestGroupMan.h
	* src/URISelector.h
	* test/FeedbackURISelectorTest.cc
	* test/InOrderURISelectorTest.cc
pull/1/head
Tatsuhiro Tsujikawa 2010-07-14 14:10:33 +00:00
parent f8bfc9e167
commit 55748de726
15 changed files with 219 additions and 94 deletions

View File

@ -1,3 +1,22 @@
2010-07-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
FeedbackURISelector now tries to chooses URI which is not used in
aria2 globally. If it is possible, it may return used URI.
* src/AdaptiveURISelector.cc
* src/AdaptiveURISelector.h
* src/CreateRequestCommand.cc
* src/FeedbackURISelector.cc
* src/FeedbackURISelector.h
* src/FileEntry.cc
* src/FileEntry.h
* src/InOrderURISelector.cc
* src/InOrderURISelector.h
* src/RequestGroupMan.cc
* src/RequestGroupMan.h
* src/URISelector.h
* test/FeedbackURISelectorTest.cc
* test/InOrderURISelectorTest.cc
2010-07-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> 2010-07-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Updated doc for options. Updated doc for options.

View File

@ -72,7 +72,8 @@ AdaptiveURISelector::AdaptiveURISelector
AdaptiveURISelector::~AdaptiveURISelector() {} AdaptiveURISelector::~AdaptiveURISelector() {}
std::string AdaptiveURISelector::select(FileEntry* fileEntry) std::string AdaptiveURISelector::select
(FileEntry* fileEntry, const std::vector<std::string>& usedHosts)
{ {
if(logger_->debug()) { if(logger_->debug()) {
logger_->debug("AdaptiveURISelector: called %d", logger_->debug("AdaptiveURISelector: called %d",

View File

@ -78,7 +78,9 @@ public:
virtual ~AdaptiveURISelector(); virtual ~AdaptiveURISelector();
virtual std::string select(FileEntry* fileEntry); virtual std::string select
(FileEntry* fileEntry, const std::vector<std::string>& usedHosts);
virtual void tuneDownloadCommand(const std::deque<std::string>& uris, virtual void tuneDownloadCommand(const std::deque<std::string>& uris,
DownloadCommand* command); DownloadCommand* command);

View File

@ -33,7 +33,6 @@
*/ */
/* copyright --> */ /* copyright --> */
#include "CreateRequestCommand.h" #include "CreateRequestCommand.h"
#include "InitiateConnectionCommandFactory.h" #include "InitiateConnectionCommandFactory.h"
#include "RequestGroup.h" #include "RequestGroup.h"
#include "Segment.h" #include "Segment.h"
@ -74,9 +73,12 @@ bool CreateRequestCommand::executeInternal()
setFileEntry(getDownloadContext()->findFileEntryByOffset setFileEntry(getDownloadContext()->findFileEntryByOffset
(getSegments().front()->getPositionToWrite())); (getSegments().front()->getPositionToWrite()));
} }
std::vector<std::string> usedHosts;
getDownloadEngine()->getRequestGroupMan()->getUsedHosts(usedHosts);
setRequest setRequest
(getFileEntry()->getRequest(getRequestGroup()->getURISelector(), (getFileEntry()->getRequest(getRequestGroup()->getURISelector(),
getOption()->getAsBool(PREF_REUSE_URI), getOption()->getAsBool(PREF_REUSE_URI),
usedHosts,
getOption()->get(PREF_REFERER), getOption()->get(PREF_REFERER),
// Don't use HEAD request when file // Don't use HEAD request when file
// size is known. // size is known.

View File

@ -60,52 +60,70 @@ public:
} }
}; };
std::string FeedbackURISelector::select(FileEntry* fileEntry) std::string FeedbackURISelector::select
(FileEntry* fileEntry, const std::vector<std::string>& usedHosts)
{ {
std::deque<std::string>& uris = fileEntry->getRemainingUris(); if(fileEntry->getRemainingUris().empty()) {
if(uris.empty()) {
return A2STR::NIL; return A2STR::NIL;
} }
// Use first 10 URIs to introduce some randomness. // Select URI with usedHosts first. If no URI is selected, then do
const int NUM_URI = 10; // it again without usedHosts.
std::string uri = selectInternal(fileEntry->getRemainingUris(), usedHosts);
if(uri.empty()) {
uri = selectInternal
(fileEntry->getRemainingUris(), std::vector<std::string>());
}
if(!uri.empty()) {
std::deque<std::string>& uris = fileEntry->getRemainingUris();
uris.erase(std::find(uris.begin(), uris.end(), uri));
}
return uri;
}
std::string FeedbackURISelector::selectInternal
(const std::deque<std::string>& uris,
const std::vector<std::string>& usedHosts)
{
// Use first 10 good URIs to introduce some randomness.
const size_t NUM_URI = 10;
// Ignore low speed server // Ignore low speed server
const unsigned int SPEED_THRESHOLD = 20*1024; const unsigned int SPEED_THRESHOLD = 20*1024;
size_t max = std::min(uris.size(), static_cast<size_t>(NUM_URI)); std::vector<std::pair<SharedHandle<ServerStat>, std::string> > fastCands;
std::deque<std::string>::iterator urisLast = uris.begin()+max; std::vector<std::string> normCands;
std::deque<std::pair<SharedHandle<ServerStat>, std::string> > cands; for(std::deque<std::string>::const_iterator i = uris.begin(),
for(std::deque<std::string>::iterator i = uris.begin(), eoi = urisLast; eoi = uris.end(); i != eoi && fastCands.size() < NUM_URI; ++i) {
i != eoi; ++i) {
Request r; Request r;
r.setUri(*i); r.setUri(*i);
SharedHandle<ServerStat> ss = serverStatMan_->find(r.getHost(), if(std::find(usedHosts.begin(), usedHosts.end(), r.getHost())
r.getProtocol()); != usedHosts.end()) {
continue;
}
SharedHandle<ServerStat> ss =
serverStatMan_->find(r.getHost(), r.getProtocol());
if(!ss.isNull() && ss->isOK() && ss->getDownloadSpeed() > SPEED_THRESHOLD) { if(!ss.isNull() && ss->isOK() && ss->getDownloadSpeed() > SPEED_THRESHOLD) {
cands.push_back(std::pair<SharedHandle<ServerStat>, std::string>(ss, *i)); fastCands.push_back(std::make_pair(ss, *i));
}
if(ss.isNull() || ss->isOK()) {
normCands.push_back(*i);
} }
} }
if(cands.empty()) { if(fastCands.empty()) {
for(std::deque<std::string>::iterator i = uris.begin(), eoi = uris.end(); if(normCands.empty()) {
i != eoi; ++i) { if(usedHosts.empty()) {
Request r; // All URIs are inspected but aria2 cannot find usable one.
r.setUri(*i); // Return first URI anyway in this case.
SharedHandle<ServerStat> ss = serverStatMan_->find(r.getHost(), return uris.front();
r.getProtocol()); } else {
// Skip ERROR state URI // If usedHosts is not empty, there is a possibility it
if(ss.isNull() || ss->isOK()) { // includes usable host.
std::string nextURI = *i; return A2STR::NIL;
uris.erase(uris.begin(), i+1);
return nextURI;
} }
} else {
return normCands.front();
} }
// All URIs are inspected but aria2 cannot find usable one.
// Return first URI anyway in this case.
std::string nextURI = uris.front();
uris.pop_front();
return nextURI;
} else { } else {
std::sort(cands.begin(), cands.end(), ServerStatFaster()); std::sort(fastCands.begin(), fastCands.end(), ServerStatFaster());
uris.erase(std::find(uris.begin(), uris.end(), cands.front().second)); return fastCands.front().second;
return cands.front().second;
} }
} }

View File

@ -45,12 +45,16 @@ class FeedbackURISelector:public URISelector {
private: private:
SharedHandle<ServerStatMan> serverStatMan_; SharedHandle<ServerStatMan> serverStatMan_;
std::string selectInternal
(const std::deque<std::string>& uris,
const std::vector<std::string>& usedHosts);
public: public:
FeedbackURISelector(const SharedHandle<ServerStatMan>& serverStatMan); FeedbackURISelector(const SharedHandle<ServerStatMan>& serverStatMan);
virtual ~FeedbackURISelector(); virtual ~FeedbackURISelector();
virtual std::string select(FileEntry* fileEntry); virtual std::string select
(FileEntry* fileEntry, const std::vector<std::string>& ignoreHosts);
}; };
} // namespace aria2 } // namespace aria2

View File

@ -102,11 +102,6 @@ void FileEntry::getUris(std::vector<std::string>& uris) const
uris.insert(uris.end(), uris_.begin(), uris_.end()); uris.insert(uris.end(), uris_.begin(), uris_.end());
} }
std::string FileEntry::selectUri(const SharedHandle<URISelector>& uriSelector)
{
return uriSelector->select(this);
}
template<typename InputIterator> template<typename InputIterator>
static size_t countInFlightHost(InputIterator first, InputIterator last, static size_t countInFlightHost(InputIterator first, InputIterator last,
const std::string& hostname) const std::string& hostname)
@ -126,51 +121,81 @@ SharedHandle<Request>
FileEntry::getRequest FileEntry::getRequest
(const SharedHandle<URISelector>& selector, (const SharedHandle<URISelector>& selector,
bool uriReuse, bool uriReuse,
const std::vector<std::string>& usedHosts,
const std::string& referer, const std::string& referer,
const std::string& method) const std::string& method)
{ {
SharedHandle<Request> req; SharedHandle<Request> req;
if(requestPool_.empty()) { Request r;
for(int g = 0; g < 2; ++g) { if(!requestPool_.empty()) {
std::vector<std::string> pending; for(std::deque<SharedHandle<Request> >::iterator i = requestPool_.begin(),
std::vector<std::string> ignoreHost; eoi = requestPool_.end(); i != eoi; ++i) {
while(1) { r.setUri((*i)->getUri());
std::string uri = selector->select(this); if(std::find(usedHosts.begin(), usedHosts.end(), r.getHost()) !=
if(uri.empty()) { usedHosts.end()) {
break; continue;
}
req.reset(new Request());
if(req->setUri(uri)) {
if(countInFlightHost(inFlightRequests_.begin(),
inFlightRequests_.end(),
req->getHost()) >= maxConnectionPerServer_) {
pending.push_back(uri);
ignoreHost.push_back(req->getHost());
req.reset();
continue;
}
req->setReferer(referer);
req->setMethod(method);
spentUris_.push_back(uri);
inFlightRequests_.push_back(req);
break;
} else {
req.reset();
}
} }
uris_.insert(uris_.begin(), pending.begin(), pending.end()); if(countInFlightHost(inFlightRequests_.begin(), inFlightRequests_.end(),
// TODO UriReuse is performed only when PREF_REUSE_URI is true. r.getHost()) >= maxConnectionPerServer_) {
if(g == 0 && uriReuse && req.isNull() && uris_.size() == pending.size()) { continue;
// Reuse URIs other than ones in pending }
reuseUri(ignoreHost); req = *i;
} else { requestPool_.erase(i);
inFlightRequests_.push_back(req);
return req;
}
}
for(int g = 0; g < 2; ++g) {
std::vector<std::string> pending;
std::vector<std::string> ignoreHost;
while(1) {
std::string uri = selector->select(this, usedHosts);
if(uri.empty()) {
break; break;
} }
req.reset(new Request());
if(req->setUri(uri)) {
if(countInFlightHost(inFlightRequests_.begin(),
inFlightRequests_.end(),
req->getHost()) >= maxConnectionPerServer_) {
pending.push_back(uri);
ignoreHost.push_back(req->getHost());
req.reset();
continue;
}
req->setReferer(referer);
req->setMethod(method);
spentUris_.push_back(uri);
inFlightRequests_.push_back(req);
break;
} else {
req.reset();
}
}
uris_.insert(uris_.begin(), pending.begin(), pending.end());
// TODO UriReuse is performed only when PREF_REUSE_URI is true.
if(g == 0 && uriReuse && req.isNull() && uris_.size() == pending.size()) {
// Reuse URIs other than ones in pending
reuseUri(ignoreHost);
} else {
break;
}
}
if(req.isNull()) {
Request r;
for(std::deque<SharedHandle<Request> >::iterator i = requestPool_.begin(),
eoi = requestPool_.end(); i != eoi; ++i) {
r.setUri((*i)->getUri());
if(countInFlightHost(inFlightRequests_.begin(), inFlightRequests_.end(),
r.getHost()) >= maxConnectionPerServer_) {
continue;
}
req = *i;
requestPool_.erase(i);
inFlightRequests_.push_back(req);
return req;
} }
} else {
req = requestPool_.front();
requestPool_.pop_front();
inFlightRequests_.push_back(req);
} }
return req; return req;
} }

View File

@ -154,8 +154,6 @@ public:
const std::string& getContentType() const { return contentType_; } const std::string& getContentType() const { return contentType_; }
std::string selectUri(const SharedHandle<URISelector>& uriSelector);
// If pooled Request object is available, one of them is removed // If pooled Request object is available, one of them is removed
// from the pool and returned. If pool is empty, then select URI // from the pool and returned. If pool is empty, then select URI
// using selectUri(selector) and construct Request object using it // using selectUri(selector) and construct Request object using it
@ -170,6 +168,7 @@ public:
SharedHandle<Request> getRequest SharedHandle<Request> getRequest
(const SharedHandle<URISelector>& selector, (const SharedHandle<URISelector>& selector,
bool uriReuse = true, bool uriReuse = true,
const std::vector<std::string>& usedHosts = std::vector<std::string>(),
const std::string& referer = A2STR::NIL, const std::string& referer = A2STR::NIL,
const std::string& method = Request::METHOD_GET); const std::string& method = Request::METHOD_GET);

View File

@ -42,7 +42,8 @@ InOrderURISelector::InOrderURISelector() {}
InOrderURISelector::~InOrderURISelector() {} InOrderURISelector::~InOrderURISelector() {}
std::string InOrderURISelector::select(FileEntry* fileEntry) std::string InOrderURISelector::select
(FileEntry* fileEntry, const std::vector<std::string>& usedHosts)
{ {
std::deque<std::string>& uris = fileEntry->getRemainingUris(); std::deque<std::string>& uris = fileEntry->getRemainingUris();
if(uris.empty()) { if(uris.empty()) {

View File

@ -44,7 +44,8 @@ public:
virtual ~InOrderURISelector(); virtual ~InOrderURISelector();
virtual std::string select(FileEntry* fileEntry); virtual std::string select
(FileEntry* fileEntry, const std::vector<std::string>& usedHosts);
}; };
} // namespace aria2 } // namespace aria2

View File

@ -896,4 +896,20 @@ bool RequestGroupMan::doesOverallUploadSpeedExceed()
maxOverallUploadSpeedLimit_ < calculateStat().getUploadSpeed(); maxOverallUploadSpeedLimit_ < calculateStat().getUploadSpeed();
} }
void RequestGroupMan::getUsedHosts(std::vector<std::string>& usedHosts)
{
Request r;
for(std::deque<SharedHandle<RequestGroup> >::const_iterator i =
requestGroups_.begin(), eoi = requestGroups_.end(); i != eoi; ++i) {
const std::deque<SharedHandle<Request> >& inFlightReqs =
(*i)->getDownloadContext()->getFirstFileEntry()->getInFlightRequests();
for(std::deque<SharedHandle<Request> >::const_iterator j =
inFlightReqs.begin(), eoj = inFlightReqs.end(); j != eoj; ++j) {
if(r.setUri((*j)->getUri())) {
usedHosts.push_back(r.getHost());
}
}
}
}
} // namespace aria2 } // namespace aria2

View File

@ -273,6 +273,8 @@ public:
{ {
return queueCheck_; return queueCheck_;
} }
void getUsedHosts(std::vector<std::string>& usedHosts);
}; };
typedef SharedHandle<RequestGroupMan> RequestGroupManHandle; typedef SharedHandle<RequestGroupMan> RequestGroupManHandle;

View File

@ -35,7 +35,9 @@
#ifndef _D_URI_SELECTOR_H_ #ifndef _D_URI_SELECTOR_H_
#define _D_URI_SELECTOR_H_ #define _D_URI_SELECTOR_H_
#include "common.h" #include "common.h"
#include <string> #include <string>
#include <vector>
#include <deque> #include <deque>
namespace aria2 { namespace aria2 {
@ -47,7 +49,8 @@ class URISelector {
public: public:
virtual ~URISelector() {} virtual ~URISelector() {}
virtual std::string select(FileEntry* fileEntry) = 0; virtual std::string select
(FileEntry* fileEntry, const std::vector<std::string>& usedHosts) = 0;
virtual void tuneDownloadCommand(const std::deque<std::string>& uris, virtual void tuneDownloadCommand(const std::deque<std::string>& uris,
DownloadCommand* command) {}; DownloadCommand* command) {};

View File

@ -16,6 +16,7 @@ class FeedbackURISelectorTest:public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FeedbackURISelectorTest); CPPUNIT_TEST_SUITE(FeedbackURISelectorTest);
CPPUNIT_TEST(testSelect_withoutServerStat); CPPUNIT_TEST(testSelect_withoutServerStat);
CPPUNIT_TEST(testSelect); CPPUNIT_TEST(testSelect);
CPPUNIT_TEST(testSelect_withUsedHosts);
CPPUNIT_TEST(testSelect_skipErrorHost); CPPUNIT_TEST(testSelect_skipErrorHost);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
private: private:
@ -48,6 +49,8 @@ public:
void testSelect(); void testSelect();
void testSelect_withUsedHosts();
void testSelect_skipErrorHost(); void testSelect_skipErrorHost();
}; };
@ -56,8 +59,9 @@ CPPUNIT_TEST_SUITE_REGISTRATION(FeedbackURISelectorTest);
void FeedbackURISelectorTest::testSelect_withoutServerStat() void FeedbackURISelectorTest::testSelect_withoutServerStat()
{ {
std::vector<std::string> usedHosts;
// Without ServerStat, selector returns first URI // Without ServerStat, selector returns first URI
std::string uri = sel->select(&fileEntry_); std::string uri = sel->select(&fileEntry_, usedHosts);
CPPUNIT_ASSERT_EQUAL(std::string("http://alpha/file"), uri); CPPUNIT_ASSERT_EQUAL(std::string("http://alpha/file"), uri);
CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry_.getRemainingUris().size()); CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry_.getRemainingUris().size());
} }
@ -71,34 +75,61 @@ void FeedbackURISelectorTest::testSelect()
SharedHandle<ServerStat> alphaHTTP(new ServerStat("alpha", "http")); SharedHandle<ServerStat> alphaHTTP(new ServerStat("alpha", "http"));
alphaHTTP->updateDownloadSpeed(180000); alphaHTTP->updateDownloadSpeed(180000);
alphaHTTP->setError(); alphaHTTP->setError();
std::vector<std::string> usedHosts;
ssm->add(bravo); ssm->add(bravo);
ssm->add(alphaFTP); ssm->add(alphaFTP);
ssm->add(alphaHTTP); ssm->add(alphaHTTP);
CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"), CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"),
sel->select(&fileEntry_)); sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry_.getRemainingUris().size()); CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry_.getRemainingUris().size());
CPPUNIT_ASSERT_EQUAL(std::string("ftp://alpha/file"), CPPUNIT_ASSERT_EQUAL(std::string("ftp://alpha/file"),
sel->select(&fileEntry_)); sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL((size_t)1, fileEntry_.getRemainingUris().size()); CPPUNIT_ASSERT_EQUAL((size_t)1, fileEntry_.getRemainingUris().size());
} }
void FeedbackURISelectorTest::testSelect_withUsedHosts()
{
SharedHandle<ServerStat> bravo(new ServerStat("bravo", "http"));
bravo->updateDownloadSpeed(100000);
SharedHandle<ServerStat> alphaHTTP(new ServerStat("alpha", "http"));
alphaHTTP->updateDownloadSpeed(180000);
alphaHTTP->setError();
std::vector<std::string> usedHosts;
usedHosts.push_back("bravo");
ssm->add(bravo);
ssm->add(alphaHTTP);
CPPUNIT_ASSERT_EQUAL(std::string("ftp://alpha/file"),
sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry_.getRemainingUris().size());
CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"),
sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL((size_t)1, fileEntry_.getRemainingUris().size());
CPPUNIT_ASSERT_EQUAL(std::string("http://alpha/file"),
sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL((size_t)0, fileEntry_.getRemainingUris().size());
}
void FeedbackURISelectorTest::testSelect_skipErrorHost() void FeedbackURISelectorTest::testSelect_skipErrorHost()
{ {
SharedHandle<ServerStat> alphaHTTP(new ServerStat("alpha", "http")); SharedHandle<ServerStat> alphaHTTP(new ServerStat("alpha", "http"));
alphaHTTP->setError(); alphaHTTP->setError();
SharedHandle<ServerStat> alphaFTP(new ServerStat("alpha", "ftp")); SharedHandle<ServerStat> alphaFTP(new ServerStat("alpha", "ftp"));
alphaFTP->setError(); alphaFTP->setError();
std::vector<std::string> usedHosts;
ssm->add(alphaHTTP); ssm->add(alphaHTTP);
ssm->add(alphaFTP); ssm->add(alphaFTP);
// See error URIs are removed from URI List.
CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"), CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"),
sel->select(&fileEntry_)); sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL((size_t)0, fileEntry_.getRemainingUris().size()); CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntry_.getRemainingUris().size());
} }
} // namespace aria2 } // namespace aria2

View File

@ -45,13 +45,14 @@ CPPUNIT_TEST_SUITE_REGISTRATION(InOrderURISelectorTest);
void InOrderURISelectorTest::testSelect() void InOrderURISelectorTest::testSelect()
{ {
std::vector<std::string> usedHosts;
CPPUNIT_ASSERT_EQUAL(std::string("http://alpha/file"), CPPUNIT_ASSERT_EQUAL(std::string("http://alpha/file"),
sel->select(&fileEntry_)); sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL(std::string("ftp://alpha/file"), CPPUNIT_ASSERT_EQUAL(std::string("ftp://alpha/file"),
sel->select(&fileEntry_)); sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"), CPPUNIT_ASSERT_EQUAL(std::string("http://bravo/file"),
sel->select(&fileEntry_)); sel->select(&fileEntry_, usedHosts));
CPPUNIT_ASSERT_EQUAL(std::string(""), sel->select(&fileEntry_)); CPPUNIT_ASSERT_EQUAL(std::string(""), sel->select(&fileEntry_, usedHosts));
} }
} // namespace aria2 } // namespace aria2