Do some simple queue empty checks in the main outgoing udp loop instead of always performing these on a separate fired thread.

This appears to improve cpu usage since launching a new thread is more expensive than performing a small amount of inline logic.
However, needs testing at scale.
cpu-performance
Justin Clark-Casey (justincc) 2013-07-18 21:28:36 +01:00
parent 553d9cc5d2
commit 8c6761c152
5 changed files with 92 additions and 22 deletions

View File

@ -485,6 +485,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_udpServer = udpServer; m_udpServer = udpServer;
m_udpClient = udpClient; m_udpClient = udpClient;
m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnQueueEmpty += HandleQueueEmpty;
m_udpClient.HasUpdates += HandleHasUpdates;
m_udpClient.OnPacketStats += PopulateStats; m_udpClient.OnPacketStats += PopulateStats;
m_prioritizer = new Prioritizer(m_scene); m_prioritizer = new Prioritizer(m_scene);
@ -4133,8 +4134,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
{ {
// if (!m_udpServer.IsRunningOutbound)
// return;
if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
{ {
// if (!m_udpServer.IsRunningOutbound)
// return;
if (m_maxUpdates == 0 || m_LastQueueFill == 0) if (m_maxUpdates == 0 || m_LastQueueFill == 0)
{ {
m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; m_maxUpdates = m_udpServer.PrimUpdatesPerCallback;
@ -4160,6 +4167,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit);
} }
internal bool HandleHasUpdates(ThrottleOutPacketTypeFlags categories)
{
bool hasUpdates = false;
if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
{
if (m_entityUpdates.Count > 0)
hasUpdates = true;
else if (m_entityProps.Count > 0)
hasUpdates = true;
}
if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
{
if (ImageManager.HasUpdates())
hasUpdates = true;
}
return hasUpdates;
}
public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID)
{ {
AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); AssetUploadCompletePacket newPack = new AssetUploadCompletePacket();

View File

@ -206,6 +206,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
public bool HasUpdates()
{
J2KImage image = GetHighestPriorityImage();
return image != null && image.IsDecoded;
}
public bool ProcessImageQueue(int packetsToSend) public bool ProcessImageQueue(int packetsToSend)
{ {
int packetsSent = 0; int packetsSent = 0;

View File

@ -31,6 +31,7 @@ using System.Net;
using System.Threading; using System.Threading;
using log4net; using log4net;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Monitoring;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Packets; using OpenMetaverse.Packets;
@ -81,6 +82,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// hooked to put more data on the empty queue</summary> /// hooked to put more data on the empty queue</summary>
public event QueueEmpty OnQueueEmpty; public event QueueEmpty OnQueueEmpty;
public event Func<ThrottleOutPacketTypeFlags, bool> HasUpdates;
/// <summary>AgentID for this client</summary> /// <summary>AgentID for this client</summary>
public readonly UUID AgentID; public readonly UUID AgentID;
/// <summary>The remote address of the connected client</summary> /// <summary>The remote address of the connected client</summary>
@ -613,15 +616,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="categories">Throttle categories to fire the callback for</param> /// <param name="categories">Throttle categories to fire the callback for</param>
private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
{ {
if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) // if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
{ {
m_isQueueEmptyRunning = true;
int start = Environment.TickCount & Int32.MaxValue;
const int MIN_CALLBACK_MS = 30;
m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
if (m_nextOnQueueEmpty == 0)
m_nextOnQueueEmpty = 1;
// Use a value of 0 to signal that FireQueueEmpty is running // Use a value of 0 to signal that FireQueueEmpty is running
m_nextOnQueueEmpty = 0; // m_nextOnQueueEmpty = 0;
// Asynchronously run the callback
Util.FireAndForget(FireQueueEmpty, categories); m_categories = categories;
if (HasUpdates(m_categories))
{
// Asynchronously run the callback
Util.FireAndForget(FireQueueEmpty, categories);
}
else
{
m_isQueueEmptyRunning = false;
}
} }
} }
private bool m_isQueueEmptyRunning;
private ThrottleOutPacketTypeFlags m_categories = 0;
/// <summary> /// <summary>
/// Fires the OnQueueEmpty callback and sets the minimum time that it /// Fires the OnQueueEmpty callback and sets the minimum time that it
/// can be called again /// can be called again
@ -631,22 +657,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// signature</param> /// signature</param>
private void FireQueueEmpty(object o) private void FireQueueEmpty(object o)
{ {
const int MIN_CALLBACK_MS = 30; // int start = Environment.TickCount & Int32.MaxValue;
// const int MIN_CALLBACK_MS = 30;
ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; // if (m_udpServer.IsRunningOutbound)
QueueEmpty callback = OnQueueEmpty; // {
ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
int start = Environment.TickCount & Int32.MaxValue; QueueEmpty callback = OnQueueEmpty;
if (callback != null) if (callback != null)
{ {
try { callback(categories); } // if (m_udpServer.IsRunningOutbound)
catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } // {
} try { callback(categories); }
catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
// }
}
// }
m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; // m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
if (m_nextOnQueueEmpty == 0) // if (m_nextOnQueueEmpty == 0)
m_nextOnQueueEmpty = 1; // m_nextOnQueueEmpty = 1;
// }
m_isQueueEmptyRunning = false;
} }
/// <summary> /// <summary>

View File

@ -1662,8 +1662,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Action generic every round // Action generic every round
Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler; Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler;
// while (true) while (true)
while (base.IsRunningOutbound) // while (base.IsRunningOutbound)
{ {
try try
{ {

View File

@ -308,8 +308,8 @@ namespace OpenMetaverse
public void AsyncBeginSend(UDPPacketBuffer buf) public void AsyncBeginSend(UDPPacketBuffer buf)
{ {
if (IsRunningOutbound) // if (IsRunningOutbound)
{ // {
try try
{ {
m_udpSocket.BeginSendTo( m_udpSocket.BeginSendTo(
@ -323,7 +323,7 @@ namespace OpenMetaverse
} }
catch (SocketException) { } catch (SocketException) { }
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
} // }
} }
void AsyncEndSend(IAsyncResult result) void AsyncEndSend(IAsyncResult result)