Merge branch 'mb-throttle-test'

Merge in the new throttle code.
sedebug
Mic Bowman 2014-12-30 16:26:57 -08:00
commit bc7570e59c
7 changed files with 156 additions and 37 deletions

View File

@ -229,7 +229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_throttleClient m_throttleClient
= new AdaptiveTokenBucket( = new AdaptiveTokenBucket(
string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name), string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name),
parentThrottle, 0, rates.Total, rates.AdaptiveThrottlesEnabled); parentThrottle, 0, rates.Total, rates.MinimumAdaptiveThrottleRate, rates.AdaptiveThrottlesEnabled);
// Create an array of token buckets for this clients different throttle categories // Create an array of token buckets for this clients different throttle categories
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];

View File

@ -107,6 +107,62 @@ namespace OpenMetaverse
/// </summary> /// </summary>
public float AverageReceiveTicksForLastSamplePeriod { get; private set; } public float AverageReceiveTicksForLastSamplePeriod { get; private set; }
#region PacketDropDebugging
/// <summary>
/// For debugging purposes only... random number generator for dropping
/// outbound packets.
/// </summary>
private Random m_dropRandomGenerator;
/// <summary>
/// For debugging purposes only... parameters for a simplified
/// model of packet loss with bursts, overall drop rate should
/// be roughly 1 - m_dropLengthProbability / (m_dropProbabiliy + m_dropLengthProbability)
/// which is about 1% for parameters 0.0015 and 0.15
/// </summary>
private double m_dropProbability = 0.0030;
private double m_dropLengthProbability = 0.15;
private bool m_dropState = false;
/// <summary>
/// For debugging purposes only... parameters to control the time
/// duration over which packet loss bursts can occur, if no packets
/// have been sent for m_dropResetTicks milliseconds, then reset the
/// state of the packet dropper to its default.
/// </summary>
private int m_dropLastTick = 0;
private int m_dropResetTicks = 500;
/// <summary>
/// Debugging code used to simulate dropped packets with bursts
/// </summary>
private bool DropOutgoingPacket()
{
double rnum = m_dropRandomGenerator.NextDouble();
// if the connection has been idle for awhile (more than m_dropResetTicks) then
// reset the state to the default state, don't continue a burst
int curtick = Util.EnvironmentTickCount();
if (Util.EnvironmentTickCountSubtract(curtick, m_dropLastTick) > m_dropResetTicks)
m_dropState = false;
m_dropLastTick = curtick;
// if we are dropping packets, then the probability of dropping
// this packet is the probability that we stay in the burst
if (m_dropState)
{
m_dropState = (rnum < (1.0 - m_dropLengthProbability)) ? true : false;
}
else
{
m_dropState = (rnum < m_dropProbability) ? true : false;
}
return m_dropState;
}
#endregion PacketDropDebugging
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
@ -117,6 +173,10 @@ namespace OpenMetaverse
{ {
m_localBindAddress = bindAddress; m_localBindAddress = bindAddress;
m_udpPort = port; m_udpPort = port;
// for debugging purposes only, initializes the random number generator
// used for simulating packet loss
// m_dropRandomGenerator = new Random();
} }
/// <summary> /// <summary>
@ -395,6 +455,12 @@ namespace OpenMetaverse
{ {
// if (IsRunningOutbound) // if (IsRunningOutbound)
// { // {
// This is strictly for debugging purposes to simulate dropped
// packets when testing throttles & retransmission code
// if (DropOutgoingPacket())
// return;
try try
{ {
m_udpSocket.BeginSendTo( m_udpSocket.BeginSendTo(

View File

@ -141,7 +141,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
udpServer.Throttle.DebugLevel = 1; udpServer.Throttle.DebugLevel = 1;
udpClient.ThrottleDebugLevel = 1; udpClient.ThrottleDebugLevel = 1;
int resendBytes = 1000; int resendBytes = 1000;
int landBytes = 2000; int landBytes = 2000;
int windBytes = 3000; int windBytes = 3000;
@ -173,6 +173,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
IniConfigSource ics = new IniConfigSource(); IniConfigSource ics = new IniConfigSource();
IConfig config = ics.AddConfig("ClientStack.LindenUDP"); IConfig config = ics.AddConfig("ClientStack.LindenUDP");
config.Set("enable_adaptive_throttles", true); config.Set("enable_adaptive_throttles", true);
config.Set("adaptive_throttle_min_bps", 32000);
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics); TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics);
ScenePresence sp ScenePresence sp
@ -184,8 +186,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
udpServer.Throttle.DebugLevel = 1; udpServer.Throttle.DebugLevel = 1;
udpClient.ThrottleDebugLevel = 1; udpClient.ThrottleDebugLevel = 1;
// Total is 280000 // Total is 275000
int resendBytes = 10000; int resendBytes = 5000; // this is set low to test the minimum throttle override
int landBytes = 20000; int landBytes = 20000;
int windBytes = 30000; int windBytes = 30000;
int cloudBytes = 40000; int cloudBytes = 40000;
@ -197,28 +199,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
SetThrottles( SetThrottles(
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
// Ratio of current adaptive drip rate to requested bytes // Ratio of current adaptive drip rate to requested bytes, minimum rate is 32000
// XXX: Should hard code this as below so we don't rely on values given by tested code to construct double commitRatio = 32000.0 / totalBytes;
// expected values.
double commitRatio = (double)udpClient.FlowThrottle.AdjustedDripRate / udpClient.FlowThrottle.TargetDripRate;
AssertThrottles( AssertThrottles(
udpClient, udpClient,
LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio, LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0); textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
// Test an increase in target throttle // Test an increase in target throttle, ack of 20 packets adds 20 * LLUDPServer.MTU bytes
udpClient.FlowThrottle.AcknowledgePackets(35000); // to the throttle, recompute commitratio from those numbers
commitRatio = 0.2; udpClient.FlowThrottle.AcknowledgePackets(20);
commitRatio = (32000.0 + 20.0 * LLUDPServer.MTU) / totalBytes;
AssertThrottles( AssertThrottles(
udpClient, udpClient,
resendBytes * commitRatio, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio, LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0); textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
// Test a decrease in target throttle // Test a decrease in target throttle, adaptive throttle should cut the rate by 50% with a floor
// set by the minimum adaptive rate
udpClient.FlowThrottle.ExpirePackets(1); udpClient.FlowThrottle.ExpirePackets(1);
commitRatio = 0.1; commitRatio = (32000.0 + (20.0 * LLUDPServer.MTU)/Math.Pow(2,1)) / totalBytes;
AssertThrottles( AssertThrottles(
udpClient, udpClient,

View File

@ -58,7 +58,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Flag used to enable adaptive throttles</summary> /// <summary>Flag used to enable adaptive throttles</summary>
public bool AdaptiveThrottlesEnabled; public bool AdaptiveThrottlesEnabled;
/// <summary>
/// Set the minimum rate that the adaptive throttles can set. The viewer
/// can still throttle lower than this, but the adaptive throttles will
/// never decrease rates below this no matter how many packets are dropped
/// </summary>
public Int64 MinimumAdaptiveThrottleRate;
/// <summary>Amount of the texture throttle to steal for the task throttle</summary> /// <summary>Amount of the texture throttle to steal for the task throttle</summary>
public double CannibalizeTextureRate; public double CannibalizeTextureRate;
@ -84,7 +91,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Total = throttleConfig.GetInt("client_throttle_max_bps", 0); Total = throttleConfig.GetInt("client_throttle_max_bps", 0);
AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false);
MinimumAdaptiveThrottleRate = throttleConfig.GetInt("adaptive_throttle_min_bps", 32000);
CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f); CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f);
CannibalizeTextureRate = Util.Clamp<double>(CannibalizeTextureRate,0.0, 0.9); CannibalizeTextureRate = Util.Clamp<double>(CannibalizeTextureRate,0.0, 0.9);
} }

View File

@ -61,7 +61,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary> /// <summary>
/// </summary> /// </summary>
protected const Int32 m_minimumDripRate = 1400; protected const Int32 m_minimumDripRate = LLUDPServer.MTU;
/// <summary>Time of the last drip, in system ticks</summary> /// <summary>Time of the last drip, in system ticks</summary>
protected Int32 m_lastDrip; protected Int32 m_lastDrip;
@ -392,13 +392,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
/// <summary> /// <summary>
/// The minimum rate for flow control. Minimum drip rate is one /// The minimum rate for adaptive flow control.
/// packet per second. Open the throttle to 15 packets per second
/// or about 160kbps.
/// </summary> /// </summary>
protected const Int64 m_minimumFlow = m_minimumDripRate * 15; protected Int64 m_minimumFlow = 32000;
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, bool enabled) /// <summary>
/// Constructor for the AdaptiveTokenBucket class
/// <param name="identifier">Unique identifier for the client</param>
/// <param name="parent">Parent bucket in the hierarchy</param>
/// <param name="requestedDripRate"></param>
/// <param name="maxDripRate">The ceiling rate for adaptation</param>
/// <param name="minDripRate">The floor rate for adaptation</param>
/// </summary>
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, Int64 minDripRate, bool enabled)
: base(identifier, parent, requestedDripRate, maxDripRate) : base(identifier, parent, requestedDripRate, maxDripRate)
{ {
AdaptiveEnabled = enabled; AdaptiveEnabled = enabled;
@ -406,34 +412,53 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (AdaptiveEnabled) if (AdaptiveEnabled)
{ {
// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); // m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
m_minimumFlow = minDripRate;
TargetDripRate = m_minimumFlow; TargetDripRate = m_minimumFlow;
AdjustedDripRate = m_minimumFlow; AdjustedDripRate = m_minimumFlow;
} }
} }
// <summary> /// <summary>
// Reliable packets sent to the client for which we never received an ack adjust the drip rate down. /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
// </summary> /// <param name="packets">Number of packets that expired without successful delivery</param>
public void ExpirePackets(Int32 count) /// </summary>
public void ExpirePackets(Int32 packets)
{ {
if (AdaptiveEnabled) if (AdaptiveEnabled)
{ {
if (DebugLevel > 0) if (DebugLevel > 0)
m_log.WarnFormat( m_log.WarnFormat(
"[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}", "[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}",
AdjustedDripRate, count, Identifier); AdjustedDripRate, packets, Identifier);
AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); // AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,packets));
// Compute the fallback solely on the rate allocated beyond the minimum, this
// should smooth out the fallback to the minimum rate
AdjustedDripRate = m_minimumFlow + (Int64) ((AdjustedDripRate - m_minimumFlow) / Math.Pow(2, packets));
} }
} }
// <summary> /// <summary>
// Reliable packets acked by the client adjust the drip rate up. /// Reliable packets acked by the client adjust the drip rate up.
// </summary> /// <param name="packets">Number of packets successfully acknowledged</param>
public void AcknowledgePackets(Int32 count) /// </summary>
public void AcknowledgePackets(Int32 packets)
{ {
if (AdaptiveEnabled) if (AdaptiveEnabled)
AdjustedDripRate = AdjustedDripRate + count; AdjustedDripRate = AdjustedDripRate + packets * LLUDPServer.MTU;
}
/// <summary>
/// Adjust the minimum flow level for the adaptive throttle, this will drop adjusted
/// throttles back to the minimum levels
/// <param>minDripRate--the new minimum flow</param>
/// </summary>
public void ResetMinimumAdaptiveFlow(Int64 minDripRate)
{
m_minimumFlow = minDripRate;
TargetDripRate = m_minimumFlow;
AdjustedDripRate = m_minimumFlow;
} }
} }
} }

View File

@ -31,6 +31,9 @@ using System.Net;
using System.Threading; using System.Threading;
using OpenMetaverse; using OpenMetaverse;
//using System.Reflection;
//using log4net;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
/// <summary> /// <summary>
@ -60,6 +63,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
//private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>Holds the actual unacked packet data, sorted by sequence number</summary> /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
private Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<uint, OutgoingPacket>(); private Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<uint, OutgoingPacket>();
/// <summary>Holds packets that need to be added to the unacknowledged list</summary> /// <summary>Holds packets that need to be added to the unacknowledged list</summary>
@ -164,8 +169,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
//if (expiredPackets != null) // if (expiredPackets != null)
// m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Found {0} expired packets on timeout of {1}", expiredPackets.Count, timeoutMS); // m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Found {0} expired packets on timeout of {1}", expiredPackets.Count, timeoutMS);
return expiredPackets; return expiredPackets;
} }
@ -192,7 +197,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// As with other network applications, assume that an acknowledged packet is an // As with other network applications, assume that an acknowledged packet is an
// indication that the network can handle a little more load, speed up the transmission // indication that the network can handle a little more load, speed up the transmission
ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength); ackedPacket.Client.FlowThrottle.AcknowledgePackets(1);
// Update stats // Update stats
Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
@ -207,9 +212,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
else else
{ {
//m_log.WarnFormat("[UNACKED PACKET COLLECTION]: Could not find packet with sequence number {0} to ack", pendingAcknowledgement.SequenceNumber); // m_log.WarnFormat("[UNACKED PACKET COLLECTION]: found null packet for sequence number {0} to ack",
// pendingAcknowledgement.SequenceNumber);
} }
} }
else
{
// m_log.WarnFormat("[UNACKED PACKET COLLECTION]: Could not find packet with sequence number {0} to ack",
// pendingAcknowledgement.SequenceNumber);
}
} }
uint pendingRemove; uint pendingRemove;

View File

@ -544,6 +544,13 @@
; ;
;client_throttle_max_bps = 187500 ;client_throttle_max_bps = 187500
; Minimum bytes per second to send to any single client as a result of
; adaptive throttling. Viewer preferences set to a lower number will
; override the settin. The example given here ensures that adaptive
; throttling will never decrease per client bandwidth below 256 kbps.
;
;adaptive_throttle_min_bps = 32000
; Adaptive throttling attempts to limit network overload when multiple ; Adaptive throttling attempts to limit network overload when multiple
; clients login by starting each connection more slowly. Disabled by ; clients login by starting each connection more slowly. Disabled by
; default ; default