[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [libktorrent] /: Fix bug causing eta estimation to go wrong during uploading
From: Joris Guisson <joris.guisson () gmail ! com>
Date: 2013-08-01 9:20:12
Message-ID: E1V4p3M-0005PP-N9 () scm ! kde ! org
[Download RAW message or body]
Git commit ce7f61416b3c7c26033511ced236381b04da85a2 by Joris Guisson.
Committed on 01/08/2013 at 09:15.
Pushed by guisson into branch 'master'.
Fix bug causing eta estimation to go wrong during uploading
BUG: 320699
M +1 -0 ChangeLog
M +215 -250 src/torrent/timeestimator.cpp
M +89 -96 src/torrent/timeestimator.h
http://commits.kde.org/libktorrent/ce7f61416b3c7c26033511ced236381b04da85a2
diff --git a/ChangeLog b/ChangeLog
index 9a249a8..67770ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,7 @@ Changes in 1.4:
- Add support for TR-064 IGD's
- Make sure we do not connect to the custom IP or the routers IP (316446)
- Fix url query not getting passed to webseed download requests (318325)
+- Fix bug causing eta estimation to go wrong during uploading (320699)
Changes in 1.3.2:
- Fix bug causing authenticated peers not to get accepted
diff --git a/src/torrent/timeestimator.cpp b/src/torrent/timeestimator.cpp
index deb2731..7c2f955 100644
--- a/src/torrent/timeestimator.cpp
+++ b/src/torrent/timeestimator.cpp
@@ -27,285 +27,250 @@
namespace bt
{
- TimeEstimator::ETAlgorithm TimeEstimator::m_algorithm = ETA_KT;
-
- TimeEstimator::TimeEstimator(TorrentControl* tc)
- : m_tc(tc)
- {
- m_samples = new SampleQueue(20);
- m_lastAvg = 0;
- m_perc = -1;
- }
-
-
- TimeEstimator::~TimeEstimator()
- {
- delete m_samples;
- }
-
- Uint32 TimeEstimator::sample() const
- {
- const TorrentStats& s = m_tc->getStats();
- if(s.completed)
- {
- return s.upload_rate;
- }
- else
- {
- return s.download_rate;
- }
- }
- Uint64 TimeEstimator::bytesLeft() const
- {
- const TorrentStats& s = m_tc->getStats();
- if(s.completed)
- {
- if(s.max_share_ratio >= 0.01f)
- {
- float ratio = s.shareRatio();
- float delta = s.max_share_ratio - ratio;
- if(delta <= 0.0f)
- return -1;
- else
- return s.bytes_downloaded * delta - s.bytes_uploaded;
- }
- else
- return -1;
- }
- else
- {
- return s.bytes_left_to_download;
- }
- }
-
- int TimeEstimator::estimate()
- {
- const TorrentStats& s = m_tc->getStats();
-
-
- // in seeding mode check if we still need to seed
- if(s.completed)
- {
- if(bytesLeft() == 0 || s.max_share_ratio < 0.01f)
- return ALREADY_FINISHED;
- }
-
- // only estimate when we are running
- if(!s.running || s.paused)
- {
- return NEVER;
- }
-
-
- //ones without pre-calculations
- switch(m_algorithm)
- {
-
- case ETA_CSA:
- return estimateCSA();
-
- case ETA_GASA:
- return estimateGASA();
-
- case ETA_KT:
- return estimateKT();
-
- case ETA_MAVG:
- m_samples->push(sample());
- return estimateMAVG();
-
- case ETA_WINX:
- m_samples->push(sample());
- return estimateWINX();
- default:
- return NEVER;
- }
- }
-
- int TimeEstimator::estimateCSA()
- {
- const TorrentStats& s = m_tc->getStats();
-
- if(s.download_rate == 0)
- return NEVER;
-
- return (int)floor((float)bytesLeft() / (float)s.download_rate);
- }
-
- int TimeEstimator::estimateGASA()
- {
- const TorrentStats& s = m_tc->getStats();
-
- if(m_tc->getRunningTimeDL() > 0 && s.bytes_downloaded > 0)
- {
- Uint64 d = s.bytes_downloaded;
- if(s.imported_bytes <= s.bytes_downloaded)
- d -= s.imported_bytes;
- double avg_speed = (double) d / (double) m_tc->getRunningTimeDL();
- return (Uint32) floor((double) bytesLeft() / avg_speed);
- }
-
- return NEVER;
- }
-
- int TimeEstimator::estimateWINX()
- {
- if(m_samples->sum() > 0 && m_samples->count() > 0)
- return (Uint32) floor((double) bytesLeft() / ((double) m_samples->sum() / \
(double) m_samples->count()));
-
- return NEVER;
- }
-
- int TimeEstimator::estimateMAVG()
- {
- if(m_samples->count() > 0)
- {
- double lavg;
-
- if(m_lastAvg == 0)
- lavg = (Uint32) m_samples->sum() / m_samples->count();
- else
- lavg = m_lastAvg - ((double) m_samples->first() / (double) m_samples->count()) + \
((double) m_samples->last() / (double) m_samples->count());
-
- m_lastAvg = (Uint32) floor(lavg);
-
- if(lavg > 0)
- return (Uint32) floor((double) bytesLeft() / ((lavg + (m_samples->sum() / \
m_samples->count())) / 2));
- return NEVER;
- }
+ TimeEstimator::TimeEstimator(TorrentControl* tc)
+ : m_tc(tc)
+ {
+ m_samples = new SampleQueue(20);
+ m_lastAvg = 0;
+ m_perc = -1;
+ }
+
+
+ TimeEstimator::~TimeEstimator()
+ {
+ delete m_samples;
+ }
+
+ Uint32 TimeEstimator::sample() const
+ {
+ const TorrentStats& s = m_tc->getStats();
+ if(s.completed)
+ {
+ return s.upload_rate;
+ }
+ else
+ {
+ return s.download_rate;
+ }
+ }
+
+ Uint64 TimeEstimator::bytesLeft() const
+ {
+ const TorrentStats& s = m_tc->getStats();
+ if(s.completed)
+ {
+ if(s.max_share_ratio >= 0.01f)
+ {
+ float ratio = s.shareRatio();
+ float delta = s.max_share_ratio - ratio;
+ if(delta <= 0.0f)
+ return 0;
+
+ if(s.bytes_downloaded * delta < s.bytes_downloaded)
+ return 0;
+ else
+ return s.bytes_downloaded * delta - s.bytes_uploaded;
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ return s.bytes_left_to_download;
+ }
+ }
+
+ int TimeEstimator::estimate()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+
+ // in seeding mode check if we still need to seed
+ if(s.completed)
+ {
+ if(bytesLeft() == 0 || s.max_share_ratio < 0.01f)
+ return ALREADY_FINISHED;
+ }
+
+ // only estimate when we are running
+ if(!s.running || s.paused)
+ {
+ return NEVER;
+ }
+
+ return estimateKT();
+ }
+
+ int TimeEstimator::estimateGASA()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+ if(m_tc->getRunningTimeDL() > 0 && s.bytes_downloaded > 0)
+ {
+ Uint64 d = s.bytes_downloaded;
+ if(s.imported_bytes <= s.bytes_downloaded)
+ d -= s.imported_bytes;
+ double avg_speed = (double) d / (double) m_tc->getRunningTimeDL();
+ return (Uint32) floor((double) bytesLeft() / avg_speed);
+ }
+
+ return NEVER;
+ }
+
+ int TimeEstimator::estimateWINX()
+ {
+ if(m_samples->sum() > 0 && m_samples->count() > 0)
+ return (Uint32) floor((double) bytesLeft() / ((double) m_samples->sum() \
/ (double) m_samples->count())); +
+ return NEVER;
+ }
+
+ int TimeEstimator::estimateMAVG()
+ {
+ if(m_samples->count() > 0)
+ {
+ double lavg;
+
+ if(m_lastAvg == 0)
+ lavg = (Uint32) m_samples->sum() / m_samples->count();
+ else
+ lavg = m_lastAvg - ((double) m_samples->first() / (double) \
m_samples->count()) + ((double) m_samples->last() / (double) m_samples->count()); +
+ m_lastAvg = (Uint32) floor(lavg);
+
+ if(lavg > 0)
+ return (Uint32) floor((double) bytesLeft() / ((lavg + \
(m_samples->sum() / m_samples->count())) / 2)); +
+ return NEVER;
+ }
+
+ return NEVER;
+ }
+
+ SampleQueue::SampleQueue(int max)
+ : m_size(max), m_count(0)
+ {
+ m_samples = new Uint32[max];
+
+ for(int i = 0; i < m_size; ++i)
+ m_samples[i] = 0;
+
+ m_end = -1;
+
+ m_start = 0;
+ }
+
+ SampleQueue::~ SampleQueue()
+ {
+ delete [] m_samples;
+ }
+
+ void SampleQueue::push(Uint32 sample)
+ {
+ if(m_count < m_size)
+ {
+ //it's not full yet
+ m_samples[(++m_end) % m_size ] = sample;
+ m_count++;
- return NEVER;
- }
+ return;
+ }
- SampleQueue::SampleQueue(int max)
- : m_size(max), m_count(0)
- {
- m_samples = new Uint32[max];
+ //since it's full I'll just replace the oldest value with new one and update \
all variables. + m_end = (m_end + 1) % m_size;
+ m_start = (m_start + 1) % m_size;
+ m_samples[m_end] = sample;
+ }
- for(int i = 0; i < m_size; ++i)
- m_samples[i] = 0;
+ Uint32 SampleQueue::first()
+ {
+ return m_samples[m_start];
+ }
- m_end = -1;
+ Uint32 SampleQueue::last()
+ {
+ return m_samples[m_end];
+ }
- m_start = 0;
- }
+ bool SampleQueue::isFull()
+ {
+ return m_count >= m_size;
+ }
- SampleQueue::~ SampleQueue()
- {
- delete [] m_samples;
- }
+ int SampleQueue::count()
+ {
+ return m_count;
+ }
- void SampleQueue::push(Uint32 sample)
- {
- if(m_count < m_size)
- {
- //it's not full yet
- m_samples[(++m_end) % m_size ] = sample;
- m_count++;
+ Uint32 SampleQueue::sum()
+ {
+ Uint32 s = 0;
- return;
- }
+ for(int i = 0; i < m_count; ++i)
+ s += m_samples[i];
- //since it's full I'll just replace the oldest value with new one and update all \
variables.
- m_end = (m_end + 1) % m_size;
- m_start = (m_start + 1) % m_size;
- m_samples[m_end] = sample;
- }
+ return s;
+ }
- Uint32 SampleQueue::first()
- {
- return m_samples[m_start];
- }
+ int TimeEstimator::estimateKT()
+ {
+ const TorrentStats& s = m_tc->getStats();
- Uint32 SampleQueue::last()
- {
- return m_samples[m_end];
- }
- bool SampleQueue::isFull()
- {
- return m_count >= m_size;
- }
+ //push new sample
+ m_samples->push(sample());
- int SampleQueue::count()
- {
- return m_count;
- }
+ if(s.completed)
+ return estimateWINX();
- Uint32 SampleQueue::sum()
- {
- Uint32 s = 0;
+ double perc = (double) s.bytes_downloaded / (double) s.total_bytes;
- for(int i = 0; i < m_count; ++i)
- s += m_samples[i];
+ int percentage = (int)(perc) * 100;
- return s;
- }
+ //calculate percentage increasement
+ double delta = 1 - 1 / (perc / m_perc);
- void TimeEstimator::setAlgorithm(ETAlgorithm theValue)
- {
- m_algorithm = theValue;
- }
+ //remember last percentage
+ m_perc = perc;
- int TimeEstimator::estimateKT()
- {
- const TorrentStats& s = m_tc->getStats();
+ if(s.bytes_downloaded < 1024 * 1024 * 100 && m_samples->last() > 0) // < \
100KB + {
+ m_lastETA = estimateGASA();
+ return m_lastETA;
+ }
- //push new sample
- m_samples->push(sample());
+ if(percentage >= 99 && m_samples->last() > 0 && bytesLeft() <= 10 * 1024 * \
1024 * 1024LL) //1% of a very large torrent could be hundreds of MB so limit it to \
10MB + {
- if(s.completed)
- return estimateWINX();
+ if(!m_samples->isFull())
+ {
+ m_lastETA = estimateWINX();
- double perc = (double) s.bytes_downloaded / (double) s.total_bytes;
+ if(m_lastETA == 0)
+ m_lastETA = estimateGASA();
- int percentage = (int)(perc) * 100;
+ return m_lastETA;
+ }
+ else
+ {
+ m_lastETA = 0;
- //calculate percentage increasement
- double delta = 1 - 1 / (perc / m_perc);
+ if(delta > 0.0001)
+ m_lastETA = estimateMAVG();
- //remember last percentage
- m_perc = perc;
+ if(m_lastETA == 0)
+ m_lastETA = estimateGASA();
+ }
+ return m_lastETA;
+ }
- if(s.bytes_downloaded < 1024 * 1024 * 100 && m_samples->last() > 0) // < 100KB
- {
- m_lastETA = estimateGASA();
- return m_lastETA;
- }
+ m_lastETA = estimateGASA();
- if(percentage >= 99 && m_samples->last() > 0 && bytesLeft() <= 10 * 1024 * 1024 * \
1024LL) //1% of a very large torrent could be hundreds of MB so limit \
it to 10MB
- {
-
- if(!m_samples->isFull())
- {
- m_lastETA = estimateWINX();
-
- if(m_lastETA == 0)
- m_lastETA = estimateGASA();
-
- return m_lastETA;
- }
- else
- {
- m_lastETA = 0;
-
- if(delta > 0.0001)
- m_lastETA = estimateMAVG();
-
- if(m_lastETA == 0)
- m_lastETA = estimateGASA();
- }
-
- return m_lastETA;
- }
-
- m_lastETA = estimateGASA();
-
- return m_lastETA;
- }
+ return m_lastETA;
+ }
}
diff --git a/src/torrent/timeestimator.h b/src/torrent/timeestimator.h
index a1047d3..198e49d 100644
--- a/src/torrent/timeestimator.h
+++ b/src/torrent/timeestimator.h
@@ -1,6 +1,6 @@
/***************************************************************************
- * Copyright (C) 2006 by Ivan Vasić *
- * ivasic@gmail.com *
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -26,100 +26,93 @@
namespace bt
{
- class TorrentControl;
-
- /**
- * Simple queue class for samples. Optimized for speed and size
- * without possibility to dynamically resize itself.
- * @author Ivan Vasic <ivasic@gmail.com>
- */
- class SampleQueue
- {
- public:
- SampleQueue(int max);
- ~SampleQueue();
-
- /**
- * Inserts new sample into the queue. The oldest sample is overwritten.
- */
- void push(Uint32 sample);
-
- Uint32 first();
- Uint32 last();
-
- bool isFull();
-
- /**
- * This function will return the number of samples in queue until it counts m_size \
number of elements.
- * After this point it will always return m_size since no samples are being \
deleted.
- */
- int count();
-
- /**
- * Returns the sum of all samples.
- */
- Uint32 sum();
-
- private:
- int m_size;
- int m_count;
-
- int m_start;
- int m_end;
-
- Uint32* m_samples;
- };
-
- /**
- * ETA estimator class. It will use different algorithms for different download \
phases.
- * @author Ivan Vasic <ivasic@gmail.com>
- */
- class KTORRENT_EXPORT TimeEstimator
- {
- public:
- static const int NEVER = INT_MAX;
- static const int ALREADY_FINISHED = 0;
-
- enum ETAlgorithm
- {
- ETA_KT, //ktorrent default algorithm - combination of the following according to \
our tests
- ETA_CSA, //current speed algorithm
- ETA_GASA, //global average speed algorithm
- ETA_WINX, //window of X algorithm
- ETA_MAVG //moving average algorithm
- };
-
- TimeEstimator(TorrentControl* tc);
- ~TimeEstimator();
-
- ///Returns ETA for m_tc torrent.
- int estimate();
-
- static void setAlgorithm(ETAlgorithm theValue);
- static ETAlgorithm algorithm() { return m_algorithm; }
-
- private:
-
- int estimateCSA();
- int estimateGASA();
- int estimateWINX();
- int estimateMAVG();
- int estimateKT();
-
- Uint32 sample() const;
- Uint64 bytesLeft() const;
-
- TorrentControl* m_tc;
- SampleQueue* m_samples;
-
- Uint32 m_lastAvg;
- int m_lastETA;
-
- //last percentage
- double m_perc;
-
- static ETAlgorithm m_algorithm;
- };
+ class TorrentControl;
+
+ /**
+ * Simple queue class for samples. Optimized for speed and size
+ * without possibility to dynamically resize itself.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class SampleQueue
+ {
+ public:
+ SampleQueue(int max);
+ ~SampleQueue();
+
+ /**
+ * Inserts new sample into the queue. The oldest sample is overwritten.
+ */
+ void push(Uint32 sample);
+
+ Uint32 first();
+ Uint32 last();
+
+ bool isFull();
+
+ /**
+ * This function will return the number of samples in queue until it counts \
m_size number of elements. + * After this point it will always return m_size \
since no samples are being deleted. + */
+ int count();
+
+ /**
+ * Returns the sum of all samples.
+ */
+ Uint32 sum();
+
+ private:
+ int m_size;
+ int m_count;
+
+ int m_start;
+ int m_end;
+
+ Uint32* m_samples;
+ };
+
+ /**
+ * ETA estimator class. It will use different algorithms for different download \
phases. + * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class KTORRENT_EXPORT TimeEstimator
+ {
+ public:
+ static const int NEVER = INT_MAX;
+ static const int ALREADY_FINISHED = 0;
+
+ enum ETAlgorithm
+ {
+ ETA_KT, //ktorrent default algorithm - combination of the following \
according to our tests + ETA_CSA, //current speed algorithm
+ ETA_GASA, //global average speed algorithm
+ ETA_WINX, //window of X algorithm
+ ETA_MAVG //moving average algorithm
+ };
+
+ TimeEstimator(TorrentControl* tc);
+ ~TimeEstimator();
+
+ ///Returns ETA for m_tc torrent.
+ int estimate();
+
+ private:
+ int estimateGASA();
+ int estimateWINX();
+ int estimateMAVG();
+ int estimateKT();
+
+ Uint32 sample() const;
+ Uint64 bytesLeft() const;
+
+ TorrentControl* m_tc;
+ SampleQueue* m_samples;
+
+ Uint32 m_lastAvg;
+ int m_lastETA;
+
+ //last percentage
+ double m_perc;
+ };
}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic