more efficient way of checking for already seen packets:

- uses Environment.TickCount for all timestamps (instead of more
  costly Util.UnixTimeSinceEpoch()
- takes care of Environment.TickCount overflow (which will happens
  after 24.8 days of system uptime)
- avoids instantiating List copies for each check
- gets rid of one lock() invocation
- moves calculation of loop invariant variable out of the loop itself
0.6.6-post-fixes
Dr Scofield 2009-06-25 07:59:25 +00:00
parent a1ba9dee8d
commit 77122d7861
1 changed files with 47 additions and 29 deletions

View File

@ -90,10 +90,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// the hashing. Expiration is less common and can be allowed the // the hashing. Expiration is less common and can be allowed the
// time for a linear scan. // time for a linear scan.
// //
private Dictionary<uint, int> m_DupeTracker = private List<uint> m_alreadySeenList = new List<uint>();
new Dictionary<uint, int>(); private Dictionary<uint, int>m_alreadySeenTracker = new Dictionary<uint, int>();
private uint m_DupeTrackerWindow = 30; private int m_alreadySeenWindow = 30000;
private int m_DupeTrackerLastCheck = Environment.TickCount; private int m_lastAlreadySeenCheck = Environment.TickCount & Int32.MaxValue;
// private Dictionary<uint, int> m_DupeTracker =
// new Dictionary<uint, int>();
// private uint m_DupeTrackerWindow = 30;
// private int m_DupeTrackerLastCheck = Environment.TickCount;
// Values for the SimStatsReporter // Values for the SimStatsReporter
// //
@ -449,27 +454,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// We can't keep an unlimited record of dupes. This will prune the // We can't keep an unlimited record of dupes. This will prune the
// dictionary by age. // dictionary by age.
// //
private void PruneDupeTracker() // NOTE: this needs to be called from within lock
// (m_alreadySeenTracker) context!
private void ExpireSeenPackets()
{ {
lock (m_DupeTracker) if (m_alreadySeenList.Count < 1024)
{
if (m_DupeTracker.Count < 1024)
return; return;
if (Environment.TickCount - m_DupeTrackerLastCheck < 2000) int ticks = 0;
return; int tc = Environment.TickCount & Int32.MaxValue;
if (tc >= m_lastAlreadySeenCheck)
ticks = tc - m_lastAlreadySeenCheck;
else
ticks = Int32.MaxValue - m_lastAlreadySeenCheck + tc;
m_DupeTrackerLastCheck = Environment.TickCount; if (ticks < 2000) return;
m_lastAlreadySeenCheck = tc;
Dictionary<uint, int> packs = // we calculate the drop dead tick count here instead of
new Dictionary<uint, int>(m_DupeTracker); // in the loop: any packet with a timestamp before
// dropDeadTC can be expired
foreach (uint pack in packs.Keys) int dropDeadTC = tc - m_alreadySeenWindow;
int i = 0;
while (i < m_alreadySeenList.Count && m_alreadySeenTracker[m_alreadySeenList[i]] < dropDeadTC)
{ {
if (Util.UnixTimeSinceEpoch() - m_DupeTracker[pack] > m_alreadySeenTracker.Remove(m_alreadySeenList[i]);
m_DupeTrackerWindow) i++;
m_DupeTracker.Remove(pack);
} }
// if we dropped packet from m_alreadySeenTracker we need
// to drop them from m_alreadySeenList as well, let's do
// that in one go: the list is ordered after all.
if (i > 0)
{
m_alreadySeenList.RemoveRange(0, i);
// m_log.DebugFormat("[CLIENT]: expired {0} packets, {1}:{2} left", i, m_alreadySeenList.Count, m_alreadySeenTracker.Count);
} }
} }
@ -545,18 +563,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Type != PacketType.AgentUpdate) if (packet.Type != PacketType.AgentUpdate)
m_PacketsReceived++; m_PacketsReceived++;
PruneDupeTracker();
// Check for duplicate packets.. packets that the client is // Check for duplicate packets.. packets that the client is
// resending because it didn't receive our ack // resending because it didn't receive our ack
// //
lock (m_DupeTracker) lock (m_alreadySeenTracker)
{ {
if (m_DupeTracker.ContainsKey(packet.Header.Sequence)) ExpireSeenPackets();
if (m_alreadySeenTracker.ContainsKey(packet.Header.Sequence))
return; return;
m_DupeTracker.Add(packet.Header.Sequence, m_alreadySeenTracker.Add(packet.Header.Sequence, Environment.TickCount & Int32.MaxValue);
Util.UnixTimeSinceEpoch()); m_alreadySeenList.Add(packet.Header.Sequence);
} }
m_Client.ProcessInPacket(packet); m_Client.ProcessInPacket(packet);