[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