Browse Source

Add a concept of debt to RateLimiter, save a bit of RAM.

Adam Ierymenko 12 years ago
parent
commit
6a24ac4f00
4 changed files with 58 additions and 34 deletions
  1. 8 3
      node/Constants.hpp
  2. 3 0
      node/Network.cpp
  3. 6 3
      node/Network.hpp
  4. 41 28
      node/RateLimiter.hpp

+ 8 - 3
node/Constants.hpp

@@ -274,12 +274,17 @@ error_no_ZT_ARCH_defined;
 /**
  * Default balance preload for multicast rate limiters on a network
  */
-#define ZT_MULTICAST_DEFAULT_RATE_PRELOAD 25.0
+#define ZT_MULTICAST_DEFAULT_RATE_PRELOAD 15000.0
 
 /**
- * Absolute maximum balance for multicast rate limiters
+ * Default maximum balance for multicast rate limiters
  */
-#define ZT_MULTICAST_DEFAULT_RATE_MAX 75.0
+#define ZT_MULTICAST_DEFAULT_RATE_MAX_BALANCE 15000.0
+
+/**
+ * Default minimum balance for multicast rate limiters (max debt)
+ */
+#define ZT_MULTICAST_DEFAULT_RATE_MIN_BALANCE -15000.0
 
 /**
  * Delay between scans of the topology active peer DB for peers that need ping

+ 3 - 0
node/Network.cpp

@@ -140,6 +140,9 @@ SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t
 	// being constructed. C++ edge cases, how I love thee.
 	SharedPtr<Network> nw(new Network());
 	nw->_r = renv;
+	nw->_rlLimit.bytesPerSecond = ZT_MULTICAST_DEFAULT_BYTES_PER_SECOND;
+	nw->_rlLimit.maxBalance = ZT_MULTICAST_DEFAULT_RATE_MAX_BALANCE;
+	nw->_rlLimit.minBalance = ZT_MULTICAST_DEFAULT_RATE_MIN_BALANCE;
 	nw->_tap = new EthernetTap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr());
 	nw->_id = id;
 	nw->_lastConfigUpdate = 0;

+ 6 - 3
node/Network.hpp

@@ -440,10 +440,10 @@ public:
 		std::map<Address,RateLimiter>::iterator rl(_multicastRateLimiters.find(addr));
 		if (rl == _multicastRateLimiters.end()) {
 			RateLimiter &newrl = _multicastRateLimiters[addr];
-			newrl.init(ZT_MULTICAST_DEFAULT_BYTES_PER_SECOND,ZT_MULTICAST_DEFAULT_RATE_PRELOAD,ZT_MULTICAST_DEFAULT_RATE_MAX);
-			return newrl.gate((double)bytes);
+			newrl.init(ZT_MULTICAST_DEFAULT_RATE_PRELOAD);
+			return newrl.gate(_rlLimit,(double)bytes);
 		}
-		return rl->second.gate((double)bytes);
+		return rl->second.gate(_rlLimit,(double)bytes);
 	}
 
 private:
@@ -452,6 +452,9 @@ private:
 
 	const RuntimeEnvironment *_r;
 
+	// Rate limits for this network
+	RateLimiter::Limit _rlLimit;
+
 	// Tap and tap multicast memberships
 	EthernetTap *_tap;
 	std::set<MulticastGroup> _multicastGroups;

+ 41 - 28
node/RateLimiter.hpp

@@ -46,6 +46,31 @@ namespace ZeroTier {
 class RateLimiter
 {
 public:
+	/**
+	 * Limits to apply to a rate limiter
+	 *
+	 * Since many rate limiters may share the same fixed limit values,
+	 * save memory by breaking this out into a struct parameter that
+	 * can be passed into RateLimiter's methods.
+	 */
+	struct Limit
+	{
+		/**
+		 * Speed in bytes per second, or rate of balance accrual
+		 */
+		double bytesPerSecond;
+
+		/**
+		 * Maximum balance that can ever be accrued (should be > 0.0)
+		 */
+		double maxBalance;
+
+		/**
+		 * Minimum balance, or maximum allowable "debt" (should be <= 0.0)
+		 */
+		double minBalance;
+	};
+
 	/**
 	 * Create an uninitialized rate limiter
 	 *
@@ -54,70 +79,58 @@ public:
 	RateLimiter() throw() {}
 
 	/**
-	 * @param bytesPerSecond Bytes per second to permit (average)
 	 * @param preload Initial balance to place in account
-	 * @param max Maximum balance to permit to ever accrue (max burst)
 	 */
-	RateLimiter(double bytesPerSecond,double preload,double max)
+	RateLimiter(double preload)
 		throw()
 	{
-		init(bytesPerSecond,preload,max);
+		init(preload);
 	}
 
 	/**
 	 * Initialize or re-initialize rate limiter
 	 *
-	 * @param bytesPerSecond Bytes per second to permit (average)
 	 * @param preload Initial balance to place in account
-	 * @param max Maximum balance to permit to ever accrue (max burst)
 	 */
-	inline void init(double bytesPerSecond,double preload,double max)
+	inline void init(double preload)
 		throw()
 	{
-		_bytesPerSecond = bytesPerSecond;
 		_lastTime = Utils::nowf();
 		_balance = preload;
-		_max = max;
 	}
 
 	/**
-	 * Update balance based on current clock
-	 *
-	 * This can be called at any time to check the current balance without
-	 * affecting the behavior of gate().
+	 * Update balance based on current clock and supplied Limits bytesPerSecond and maxBalance
 	 *
+	 * @param lim Current limits in effect
 	 * @return New balance
 	 */
-	inline double updateBalance()
+	inline double updateBalance(const Limit &lim)
 		throw()
 	{
-		double now = Utils::nowf();
-		double b = _balance = fmin(_max,_balance + (_bytesPerSecond * (now - _lastTime)));
-		_lastTime = now;
-		return b;
+		double lt = _lastTime;
+		double now = _lastTime = Utils::nowf();
+		return (_balance = fmin(lim.maxBalance,_balance + (lim.bytesPerSecond * (now - lt))));
 	}
 
 	/**
-	 * Test balance and update / deduct if there is enough to transfer 'bytes'
+	 * Update balance and test if a block of 'bytes' should be permitted to be transferred
 	 *
+	 * @param lim Current limits in effect
 	 * @param bytes Number of bytes that we wish to transfer
-	 * @return True if balance was sufficient (balance is updated), false if not (balance unchanged)
+	 * @return True if balance was sufficient
 	 */
-	inline bool gate(double bytes)
+	inline bool gate(const Limit &lim,double bytes)
 		throw()
 	{
-		if (updateBalance() >= bytes) {
-			_balance -= bytes;
-			return true;
-		}
-		return false;
+		bool allow = (updateBalance(lim) >= bytes);
+		_balance = fmax(lim.minBalance,_balance - bytes);
+		return allow;
 	}
 
 private:
-	double _bytesPerSecond;
 	double _lastTime;
 	double _balance;
-	double _max;
 };
 
 } // namespace ZeroTier