From 9e05371fb6a70d7d5e9d49db31b879e79c1b56e7 Mon Sep 17 00:00:00 2001 From: oliviercommelarbre Date: Thu, 11 Feb 2016 00:04:49 +0100 Subject: [PATCH 1/2] Added support for a dynamic management of the number of concurrent downloads as a function of the overall bandwidth observed --- src/FillRequestGroupCommand.cc | 12 ++++++ src/FillRequestGroupCommand.h | 2 + src/NetStat.cc | 10 +++++ src/NetStat.h | 4 ++ src/OptionHandlerFactory.cc | 10 +++++ src/OptionHandlerImpl.cc | 55 +++++++++++++++++++++++++ src/OptionHandlerImpl.h | 11 +++++ src/RequestGroupMan.cc | 74 ++++++++++++++++++++++++++++++++-- src/RequestGroupMan.h | 21 ++++++++-- src/RpcMethodImpl.cc | 6 ++- src/SpeedCalc.cc | 27 ++++++++++++- src/SpeedCalc.h | 2 + src/prefs.cc | 6 +++ src/prefs.h | 6 +++ src/usage_text.h | 16 +++++++- 15 files changed, 252 insertions(+), 10 deletions(-) diff --git a/src/FillRequestGroupCommand.cc b/src/FillRequestGroupCommand.cc index 0c326a44..6ec4151b 100644 --- a/src/FillRequestGroupCommand.cc +++ b/src/FillRequestGroupCommand.cc @@ -42,6 +42,7 @@ #include "LogFactory.h" #include "DownloadContext.h" #include "fmt.h" +#include "wallclock.h" namespace aria2 { @@ -80,6 +81,17 @@ bool FillRequestGroupCommand::execute() } } e_->addRoutineCommand(std::unique_ptr(this)); + + // let's make sure we come back here every second or so + // if we use the optimize-concurrent-download option + if (rgman->getOptimizeConcurrentDownloads()) { + const auto& now = global::wallclock(); + if (std::chrono::duration_cast(lastExecTime.difference(now)) >= 1_s) { + lastExecTime = now; + rgman->requestQueueCheck(); + } + } + return false; } diff --git a/src/FillRequestGroupCommand.h b/src/FillRequestGroupCommand.h index 4666b452..3059156d 100644 --- a/src/FillRequestGroupCommand.h +++ b/src/FillRequestGroupCommand.h @@ -37,6 +37,7 @@ #include "Command.h" #include "a2time.h" +#include "TimerA2.h" namespace aria2 { @@ -46,6 +47,7 @@ class DownloadEngine; class FillRequestGroupCommand : public Command { private: DownloadEngine* e_; + Timer lastExecTime; public: FillRequestGroupCommand(cuid_t cuid, DownloadEngine* e); diff --git a/src/NetStat.cc b/src/NetStat.cc index ad1676b9..a7ccbd9f 100644 --- a/src/NetStat.cc +++ b/src/NetStat.cc @@ -56,6 +56,11 @@ int NetStat::calculateDownloadSpeed() return downloadSpeed_.calculateSpeed(); } +int NetStat::calculateNewestDownloadSpeed(int seconds) +{ + return downloadSpeed_.calculateNewestSpeed(seconds); +} + int NetStat::calculateAvgDownloadSpeed() { return avgDownloadSpeed_ = downloadSpeed_.calculateAvgSpeed(); @@ -63,6 +68,11 @@ int NetStat::calculateAvgDownloadSpeed() int NetStat::calculateUploadSpeed() { return uploadSpeed_.calculateSpeed(); } +int NetStat::calculateNewestUploadSpeed(int seconds) +{ + return uploadSpeed_.calculateNewestSpeed(seconds); +} + int NetStat::calculateAvgUploadSpeed() { return avgUploadSpeed_ = uploadSpeed_.calculateAvgSpeed(); diff --git a/src/NetStat.h b/src/NetStat.h index 90a8e9f0..cacf8b43 100644 --- a/src/NetStat.h +++ b/src/NetStat.h @@ -61,10 +61,14 @@ public: */ int calculateDownloadSpeed(); + int calculateNewestDownloadSpeed(int seconds); + int calculateAvgDownloadSpeed(); int calculateUploadSpeed(); + int calculateNewestUploadSpeed(int seconds); + int calculateAvgUploadSpeed(); void updateDownload(size_t bytes); diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 87b07cec..86a6f1a9 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -416,6 +416,16 @@ std::vector OptionHandlerFactory::createOptionHandlers() op->setChangeGlobalOption(true); handlers.push_back(op); } + { + OptionHandler* op(new OptimizeConcurrentDownloadsOptionHandler + (PREF_OPTIMIZE_CONCURRENT_DOWNLOADS, + TEXT_OPTIMIZE_CONCURRENT_DOWNLOADS, + A2_V_FALSE, + OptionHandler::OPT_ARG)); + op->addTag(TAG_BASIC); + op->setChangeGlobalOption(true); + handlers.push_back(op); + } { OptionHandler* op(new NumberOptionHandler(PREF_MAX_CONNECTION_PER_SERVER, TEXT_MAX_CONNECTION_PER_SERVER, diff --git a/src/OptionHandlerImpl.cc b/src/OptionHandlerImpl.cc index a01c111e..305493a2 100644 --- a/src/OptionHandlerImpl.cc +++ b/src/OptionHandlerImpl.cc @@ -586,6 +586,61 @@ std::string PrioritizePieceOptionHandler::createPossibleValuesString() const return "head[=SIZE], tail[=SIZE]"; } +OptimizeConcurrentDownloadsOptionHandler::OptimizeConcurrentDownloadsOptionHandler( + PrefPtr pref, const char* description, const std::string& defaultValue, char shortName) + : AbstractOptionHandler(pref, description, defaultValue, + OptionHandler::OPT_ARG, shortName) +{ +} + +void OptimizeConcurrentDownloadsOptionHandler::parseArg(Option& option, + const std::string& optarg) const +{ + if (optarg == "true" || optarg.empty()) { + option.put(pref_, A2_V_TRUE); + } + else if (optarg == "false") { + option.put(pref_, A2_V_FALSE); + } + else { + auto p = util::divide(std::begin(optarg), std::end(optarg), ':'); + + std::string coeff_b(p.second.first,p.second.second); + if(coeff_b.empty()) { + std::string msg = pref_->k; + msg += " "; + msg += _("must be either 'true', 'false' or a pair numeric coefficients A and B under the form 'A:B'."); + throw DL_ABORT_EX(msg); + } + + std::string coeff_a(p.first.first,p.first.second); + + + PrefPtr pref=PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA; + std::string *sptr = &coeff_a; + for(;;) { + try { + double dbl = std::stod(*sptr); + } catch(std::invalid_argument & ex) { + throw DL_ABORT_EX(fmt("Bad number '%s'", sptr->c_str())); + } + option.put(pref,*sptr); + + if(pref == PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB) { + break; + } + pref = PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB; + sptr = &coeff_b; + } + option.put(pref_, A2_V_TRUE); + } +} + +std::string OptimizeConcurrentDownloadsOptionHandler::createPossibleValuesString() const +{ + return "true, false, A:B"; +} + DeprecatedOptionHandler::DeprecatedOptionHandler( OptionHandler* depOptHandler, const OptionHandler* repOptHandler, bool stillWork, std::string additionalMessage) diff --git a/src/OptionHandlerImpl.h b/src/OptionHandlerImpl.h index dbee5a77..a31ade77 100644 --- a/src/OptionHandlerImpl.h +++ b/src/OptionHandlerImpl.h @@ -254,6 +254,17 @@ public: virtual std::string createPossibleValuesString() const CXX11_OVERRIDE; }; +class OptimizeConcurrentDownloadsOptionHandler : public AbstractOptionHandler { +public: + OptimizeConcurrentDownloadsOptionHandler( + PrefPtr pref, const char* description = NO_DESCRIPTION, + const std::string& defaultValue = NO_DEFAULT_VALUE, char shortName = 0); + virtual void parseArg(Option& option, + const std::string& optarg) const CXX11_OVERRIDE; + virtual std::string createPossibleValuesString() const CXX11_OVERRIDE; +}; + + // This class is used to deprecate option and optionally handle its // option value using replacing option. class DeprecatedOptionHandler : public OptionHandler { diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index fa7723ce..d157c382 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -83,6 +83,7 @@ #include "SimpleRandomizer.h" #include "array_fun.h" #include "OpenedFileCounter.h" +#include "wallclock.h" #ifdef ENABLE_BITTORRENT #include "bittorrent_helper.h" #endif // ENABLE_BITTORRENT @@ -102,8 +103,12 @@ void appendReservedGroup(RequestGroupList& list, InputIterator first, RequestGroupMan::RequestGroupMan( std::vector> requestGroups, - int maxSimultaneousDownloads, const Option* option) - : maxSimultaneousDownloads_(maxSimultaneousDownloads), + int maxConcurrentDownloads, const Option* option) + : maxConcurrentDownloads_(maxConcurrentDownloads), + optimizeConcurrentDownloads_(false), + optimizeConcurrentDownloadsCoeffA_(5.), + optimizeConcurrentDownloadsCoeffB_(25.), + optimizationSpeed_(0), numActive_(0), option_(option), serverStatMan_(std::make_shared()), @@ -120,12 +125,25 @@ RequestGroupMan::RequestGroupMan( this, option->getAsInt(PREF_BT_MAX_OPEN_FILES))), numStoppedTotal_(0) { + setupOptimizeConcurrentDownloads(); appendReservedGroup(reservedGroups_, requestGroups.begin(), requestGroups.end()); } RequestGroupMan::~RequestGroupMan() { openedFileCounter_->deactivate(); } +bool RequestGroupMan::setupOptimizeConcurrentDownloads(void) +{ + optimizeConcurrentDownloads_ = option_->getAsBool(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS); + if (optimizeConcurrentDownloads_) { + if (option_->defined(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA)) { + optimizeConcurrentDownloadsCoeffA_ = std::stod(option_->get(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA)); + optimizeConcurrentDownloadsCoeffB_ = std::stod(option_->get(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB)); + } + } + return optimizeConcurrentDownloads_; +} + bool RequestGroupMan::downloadFinished() { if (keepRunning_) { @@ -474,11 +492,14 @@ createInitialCommand(const std::shared_ptr& requestGroup, void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e) { removeStoppedGroup(e); - if (static_cast(maxSimultaneousDownloads_) <= numActive_) { + + int maxConcurrentDownloads = optimizeConcurrentDownloads_ ? optimizeConcurrentDownloads() : maxConcurrentDownloads_; + + if (static_cast(maxConcurrentDownloads) <= numActive_) { return; } int count = 0; - int num = maxSimultaneousDownloads_ - numActive_; + int num = maxConcurrentDownloads - numActive_; std::vector> pending; while (count < num && (uriListParser_ || !reservedGroups_.empty())) { @@ -1005,4 +1026,49 @@ void RequestGroupMan::decreaseNumActive() --numActive_; } + +int RequestGroupMan::optimizeConcurrentDownloads() +{ + // gauge the current speed + int currentSpeed = getNetStat().calculateDownloadSpeed(); + + const auto& now = global::wallclock(); + if (currentSpeed >= optimizationSpeed_) { + optimizationSpeed_ = currentSpeed; + optimizationSpeedTimer_ = now; + } else if (std::chrono::duration_cast(optimizationSpeedTimer_.difference(now)) >= 5_s) { + // we keep using the reference speed for minimum 5 seconds so reset the timer + optimizationSpeedTimer_ = now; + + // keep the reference speed as long as the speed tends to augment or to maintain itself within 10% + if (currentSpeed >= 1.1 * getNetStat().calculateNewestDownloadSpeed(5)) { + // else assume a possible congestion and record a new optimization speed by dichotomy + optimizationSpeed_ = (optimizationSpeed_ + currentSpeed)/2.; + } + } + + if (optimizationSpeed_ <= 0) { + return 1; + } + + // apply the rule + if ((maxOverallDownloadSpeedLimit_ > 0) && (optimizationSpeed_ > maxOverallDownloadSpeedLimit_)) { + optimizationSpeed_ = maxOverallDownloadSpeedLimit_; + } + int maxConcurrentDownloads = ceil( + optimizeConcurrentDownloadsCoeffA_ + + optimizeConcurrentDownloadsCoeffB_ * log10(optimizationSpeed_ * 8. / 1000000.) + ); + + // bring the value in bound between 1 and the defined maximum + maxConcurrentDownloads = std::min(std::max(1, maxConcurrentDownloads), maxConcurrentDownloads_); + + A2_LOG_DEBUG + (fmt("Max concurrent downloads optimized at %d (%lu currently active) " + "[optimization speed %sB/s, current speed %sB/s]", + maxConcurrentDownloads, numActive_, util::abbrevSize(optimizationSpeed_).c_str(), + util::abbrevSize(currentSpeed).c_str())); + + return maxConcurrentDownloads; +} } // namespace aria2 diff --git a/src/RequestGroupMan.h b/src/RequestGroupMan.h index ec728d49..566b77d3 100644 --- a/src/RequestGroupMan.h +++ b/src/RequestGroupMan.h @@ -72,7 +72,13 @@ private: RequestGroupList reservedGroups_; DownloadResultList downloadResults_; - int maxSimultaneousDownloads_; + int maxConcurrentDownloads_; + + bool optimizeConcurrentDownloads_; + double optimizeConcurrentDownloadsCoeffA_; + double optimizeConcurrentDownloadsCoeffB_; + int optimizationSpeed_; + Timer optimizationSpeedTimer_; // The number of simultaneous active downloads, excluding seed only // item if PREF_BT_DETACH_SEED_ONLY is true. We rely on this @@ -135,9 +141,11 @@ private: void addRequestGroupIndex( const std::vector>& groups); + int optimizeConcurrentDownloads(); + public: RequestGroupMan(std::vector> requestGroups, - int maxSimultaneousDownloads, const Option* option); + int maxConcurrentDownloads, const Option* option); ~RequestGroupMan(); @@ -195,6 +203,13 @@ public: bool removeReservedGroup(a2_gid_t gid); + bool getOptimizeConcurrentDownloads() const + { + return optimizeConcurrentDownloads_; + } + + bool setupOptimizeConcurrentDownloads(); + void showDownloadResults(OutputFile& o, bool full) const; bool isSameFileBeingDownloaded(RequestGroup* requestGroup) const; @@ -291,7 +306,7 @@ public: return maxOverallUploadSpeedLimit_; } - void setMaxSimultaneousDownloads(int max) { maxSimultaneousDownloads_ = max; } + void setMaxConcurrentDownloads(int max) { maxConcurrentDownloads_ = max; } // Call this function if requestGroups_ queue should be maintained. // This function is added to reduce the call of maintenance, but at diff --git a/src/RpcMethodImpl.cc b/src/RpcMethodImpl.cc index e8fccd62..5d6e8202 100644 --- a/src/RpcMethodImpl.cc +++ b/src/RpcMethodImpl.cc @@ -1592,10 +1592,14 @@ void changeGlobalOption(const Option& option, DownloadEngine* e) option.getAsInt(PREF_MAX_OVERALL_UPLOAD_LIMIT)); } if (option.defined(PREF_MAX_CONCURRENT_DOWNLOADS)) { - e->getRequestGroupMan()->setMaxSimultaneousDownloads( + e->getRequestGroupMan()->setMaxConcurrentDownloads( option.getAsInt(PREF_MAX_CONCURRENT_DOWNLOADS)); e->getRequestGroupMan()->requestQueueCheck(); } + if(option.defined(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS)) { + e->getRequestGroupMan()->setupOptimizeConcurrentDownloads(); + e->getRequestGroupMan()->requestQueueCheck(); + } if (option.defined(PREF_MAX_DOWNLOAD_RESULT)) { e->getRequestGroupMan()->setMaxDownloadResult( option.getAsInt(PREF_MAX_DOWNLOAD_RESULT)); diff --git a/src/SpeedCalc.cc b/src/SpeedCalc.cc index a2bf4ee9..2c49c9ef 100644 --- a/src/SpeedCalc.cc +++ b/src/SpeedCalc.cc @@ -42,7 +42,7 @@ namespace aria2 { namespace { -constexpr auto WINDOW_TIME = 15_s; +constexpr auto WINDOW_TIME = 10_s; } // namespace SpeedCalc::SpeedCalc() : accumulatedLength_(0), bytesWindow_(0), maxSpeed_(0) {} @@ -84,6 +84,31 @@ int SpeedCalc::calculateSpeed() return speed; } +int SpeedCalc::calculateNewestSpeed(int seconds) +{ + const auto& now = global::wallclock(); + removeStaleTimeSlot(now); + + int64_t bytesCount(0); + auto it = timeSlots_.rbegin(); + while (it != timeSlots_.rend()) { + if (it->first.difference(now) > seconds * 1_s) { + break; + } + bytesCount += (*it++).second; + } + if (it == timeSlots_.rbegin()) { + return 0; + } + + auto elapsed = std::chrono::duration_cast( + (*--it).first.difference(now)).count(); + if (elapsed <= 0) { + elapsed = 1; + } + return bytesCount * (1000. / elapsed); +} + void SpeedCalc::update(size_t bytes) { const auto& now = global::wallclock(); diff --git a/src/SpeedCalc.h b/src/SpeedCalc.h index 25eb8c6f..99f4fb00 100644 --- a/src/SpeedCalc.h +++ b/src/SpeedCalc.h @@ -61,6 +61,8 @@ public: */ int calculateSpeed(); + int calculateNewestSpeed(int seconds); + int getMaxSpeed() const { return maxSpeed_; } int calculateAvgSpeed() const; diff --git a/src/prefs.cc b/src/prefs.cc index caffd349..3206a31a 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -216,6 +216,12 @@ PrefPtr PREF_INPUT_FILE = makePref("input-file"); PrefPtr PREF_DEFERRED_INPUT = makePref("deferred-input"); // value: 1*digit PrefPtr PREF_MAX_CONCURRENT_DOWNLOADS = makePref("max-concurrent-downloads"); +// value: true | false | A:B +PrefPtr PREF_OPTIMIZE_CONCURRENT_DOWNLOADS = makePref("optimize-concurrent-downloads"); +// values: 1*digit ['.' [ 1*digit ] ] +PrefPtr PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA = makePref("optimize-concurrent-downloads-coeffA"); +// values: 1*digit ['.' [ 1*digit ] ] +PrefPtr PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB = makePref("optimize-concurrent-downloads-coeffB"); // value: true | false PrefPtr PREF_FORCE_SEQUENTIAL = makePref("force-sequential"); // value: true | false diff --git a/src/prefs.h b/src/prefs.h index 52078fe9..5e66faf7 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -173,6 +173,12 @@ extern PrefPtr PREF_DEFERRED_INPUT; // value: 1*digit extern PrefPtr PREF_MAX_CONCURRENT_DOWNLOADS; // value: true | false +extern PrefPtr PREF_OPTIMIZE_CONCURRENT_DOWNLOADS; +// value: 1*digit ['.' [ 1*digit ] ] +extern PrefPtr PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA; +// value: 1*digit ['.' [ 1*digit ] ] +extern PrefPtr PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB; +// value: true | false extern PrefPtr PREF_FORCE_SEQUENTIAL; // value: true | false extern PrefPtr PREF_AUTO_FILE_RENAMING; diff --git a/src/usage_text.h b/src/usage_text.h index 5f11fde4..e0e77f45 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -242,7 +242,21 @@ #define TEXT_MAX_CONCURRENT_DOWNLOADS \ _(" -j, --max-concurrent-downloads=N Set maximum number of parallel downloads for\n" \ " every static (HTTP/FTP) URL, torrent and metalink.\n" \ - " See also --split option.") + " See also --split and --optimize-concurrent-downloads options.") +#define TEXT_OPTIMIZE_CONCURRENT_DOWNLOADS\ + _(" --optimize-concurrent-downloads[=true|false|A:B] Optimizes the number of\n" \ + " concurrent downloads according to the bandwidth\n" \ + " available. aria2 uses the download speed observed\n" \ + " in the previous downloads to adapt the number of\n" \ + " downloads launched in parallel according to the rule\n" \ + " N = A + B Log10(speed in Mbps). The coefficients\n" \ + " A and B can be customized in the option arguments\n" \ + " with A and B separated by a colon. The default values\n" \ + " (A=5,B=25) lead to using typically 5 parallel\n" \ + " downloads on 1Mbps networks and above 50 on 100Mbps\n" \ + " networks. The number of parallel downloads remains\n" \ + " constrained under the maximum defined by the\n" \ + " max-concurrent-downloads parameter.") #define TEXT_LOAD_COOKIES \ _(" --load-cookies=FILE Load Cookies from FILE using the Firefox3 format\n" \ " and Mozilla/Firefox(1.x/2.x)/Netscape format.") From 60d0cb0e24974534c10f63cf6833119b6af7be2b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 16 Mar 2016 21:25:43 +0900 Subject: [PATCH 2/2] Remove trailing white spaces --- src/NetStat.cc | 2 +- src/RequestGroupMan.cc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NetStat.cc b/src/NetStat.cc index a7ccbd9f..98ef1b0d 100644 --- a/src/NetStat.cc +++ b/src/NetStat.cc @@ -70,7 +70,7 @@ int NetStat::calculateUploadSpeed() { return uploadSpeed_.calculateSpeed(); } int NetStat::calculateNewestUploadSpeed(int seconds) { - return uploadSpeed_.calculateNewestSpeed(seconds); + return uploadSpeed_.calculateNewestSpeed(seconds); } int NetStat::calculateAvgUploadSpeed() diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index d157c382..824e1ea2 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -136,7 +136,7 @@ bool RequestGroupMan::setupOptimizeConcurrentDownloads(void) { optimizeConcurrentDownloads_ = option_->getAsBool(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS); if (optimizeConcurrentDownloads_) { - if (option_->defined(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA)) { + if (option_->defined(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA)) { optimizeConcurrentDownloadsCoeffA_ = std::stod(option_->get(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA)); optimizeConcurrentDownloadsCoeffB_ = std::stod(option_->get(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB)); } @@ -1062,13 +1062,13 @@ int RequestGroupMan::optimizeConcurrentDownloads() // bring the value in bound between 1 and the defined maximum maxConcurrentDownloads = std::min(std::max(1, maxConcurrentDownloads), maxConcurrentDownloads_); - + A2_LOG_DEBUG (fmt("Max concurrent downloads optimized at %d (%lu currently active) " "[optimization speed %sB/s, current speed %sB/s]", - maxConcurrentDownloads, numActive_, util::abbrevSize(optimizationSpeed_).c_str(), + maxConcurrentDownloads, numActive_, util::abbrevSize(optimizationSpeed_).c_str(), util::abbrevSize(currentSpeed).c_str())); - + return maxConcurrentDownloads; } } // namespace aria2