1 /* <!-- copyright */
2 /*
3 * aria2 - The high speed download utility
4 *
5 * Copyright (C) 2006 Tatsuhiro Tsujikawa
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * In addition, as a special exception, the copyright holders give
22 * permission to link the code of portions of this program with the
23 * OpenSSL library under certain conditions as described in each
24 * individual source file, and distribute linked combinations
25 * including the two.
26 * You must obey the GNU General Public License in all respects
27 * for all of the code used other than OpenSSL. If you modify
28 * file(s) with this exception, you may extend this exception to your
29 * version of the file(s), but you are not obligated to do so. If you
30 * do not wish to do so, delete this exception statement from your
31 * version. If you delete this exception statement from all source
32 * files in the program, then also delete it here.
33 */
34 /* copyright --> */
35 #include "SpeedCalc.h"
36
37 #include <algorithm>
38 #include <functional>
39
40 #include "wallclock.h"
41
42 namespace aria2 {
43
44 namespace {
45 constexpr auto WINDOW_TIME = 10_s;
46 } // namespace
47
SpeedCalc()48 SpeedCalc::SpeedCalc() : accumulatedLength_(0), bytesWindow_(0), maxSpeed_(0) {}
49
reset()50 void SpeedCalc::reset()
51 {
52 timeSlots_.clear();
53 start_ = global::wallclock();
54 accumulatedLength_ = 0;
55 bytesWindow_ = 0;
56 maxSpeed_ = 0;
57 }
58
removeStaleTimeSlot(const Timer & now)59 void SpeedCalc::removeStaleTimeSlot(const Timer& now)
60 {
61 while (!timeSlots_.empty()) {
62 if (timeSlots_[0].first.difference(now) <= WINDOW_TIME) {
63 break;
64 }
65 bytesWindow_ -= timeSlots_[0].second;
66 timeSlots_.pop_front();
67 }
68 }
69
calculateSpeed()70 int SpeedCalc::calculateSpeed()
71 {
72 const auto& now = global::wallclock();
73 removeStaleTimeSlot(now);
74 if (timeSlots_.empty()) {
75 return 0;
76 }
77 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
78 timeSlots_[0].first.difference(now))
79 .count();
80 if (elapsed <= 0) {
81 elapsed = 1;
82 }
83 int speed = bytesWindow_ * 1000 / elapsed;
84 maxSpeed_ = std::max(speed, maxSpeed_);
85 return speed;
86 }
87
calculateNewestSpeed(int seconds)88 int SpeedCalc::calculateNewestSpeed(int seconds)
89 {
90 const auto& now = global::wallclock();
91 removeStaleTimeSlot(now);
92
93 int64_t bytesCount(0);
94 auto it = timeSlots_.rbegin();
95 while (it != timeSlots_.rend()) {
96 if (it->first.difference(now) > seconds * 1_s) {
97 break;
98 }
99 bytesCount += (*it++).second;
100 }
101 if (it == timeSlots_.rbegin()) {
102 return 0;
103 }
104
105 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
106 (*--it).first.difference(now))
107 .count();
108 if (elapsed <= 0) {
109 elapsed = 1;
110 }
111 return bytesCount * (1000. / elapsed);
112 }
113
update(size_t bytes)114 void SpeedCalc::update(size_t bytes)
115 {
116 const auto& now = global::wallclock();
117 removeStaleTimeSlot(now);
118 if (timeSlots_.empty() ||
119 std::chrono::duration_cast<std::chrono::seconds>(
120 timeSlots_.back().first.difference(now)) >= 1_s) {
121 timeSlots_.push_back(std::make_pair(now, bytes));
122 }
123 else {
124 timeSlots_.back().second += bytes;
125 }
126 bytesWindow_ += bytes;
127 accumulatedLength_ += bytes;
128 }
129
calculateAvgSpeed() const130 int SpeedCalc::calculateAvgSpeed() const
131 {
132 auto milliElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
133 start_.difference(global::wallclock()))
134 .count();
135 // if milliElapsed is too small, the average speed is rubbish, better
136 // return 0
137 if (milliElapsed > 4) {
138 int speed = accumulatedLength_ * 1000 / milliElapsed;
139 return speed;
140 }
141 else {
142 return 0;
143 }
144 }
145
146 } // namespace aria2
147