Skip to content

Commit

Permalink
Stats cleanup/refactor.
Browse files Browse the repository at this point in the history
Added PingHistogram, QualityHistogram, and JitterHistogram structs.  Moved
some logic in there as methods, and fixed some duplicaetd code.

Don't assume a PingTracker wants to collect a detailed histogram.  (There
is a use case in the code to deal with relays where this is not useful.)
  • Loading branch information
zpostfacto committed Mar 11, 2019
1 parent b3182a5 commit 578261c
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 301 deletions.
167 changes: 108 additions & 59 deletions src/steamnetworkingsockets/steamnetworking_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,111 @@ struct SteamDatagramLinkInstantaneousStats
void Clear();
};

/// Counts of ping times by bucket
struct PingHistogram
{
int m_n25, m_n50, m_n75, m_n100, m_n125, m_n150, m_n200, m_n300, m_nMax;

void Reset() { memset( this, 0, sizeof(*this) ); }

void AddSample( int nPingMS )
{

// Update histogram using hand-rolled sort-of-binary-search, optimized
// for the expectation that most pings will be reasonable
if ( nPingMS <= 100 )
{
if ( nPingMS <= 50 )
{
if ( nPingMS <= 25 )
++m_n25;
else
++m_n50;
}
else
{
if ( nPingMS <= 75 )
++m_n75;
else
++m_n100;
}
}
else
{
if ( nPingMS <= 150 )
{
if ( nPingMS <= 125 )
++m_n125;
else
++m_n150;
}
else
{
if ( nPingMS <= 200 )
++m_n200;
else if ( nPingMS <= 300 )
++m_n300;
else
++m_nMax;
}
}
}

inline int TotalCount() const
{
return m_n25 + m_n50 + m_n75 + m_n100 + m_n125 + m_n150 + m_n200 + m_n300 + m_nMax;
}
};

/// Count of quality measurement intervals by bucket
struct QualityHistogram
{
int m_n100, m_n99, m_n97, m_n95, m_n90, m_n75, m_n50, m_n1, m_nDead;

void Reset() { memset( this, 0, sizeof(*this) ); }

inline int TotalCount() const
{
return m_n100 + m_n99 + m_n97 + m_n95 + m_n90 + m_n75 + m_n50 + m_n1 + m_nDead;
}
};

/// Counts of jitter values by bucket
struct JitterHistogram
{
void Reset() { memset( this, 0, sizeof(*this) ); }

int m_nNegligible; // <1ms
int m_n1; // 1--2ms
int m_n2; // 2--5ms
int m_n5; // 5--10ms
int m_n10; // 10--20ms
int m_n20; // 20ms or more

void AddSample( SteamNetworkingMicroseconds usecJitter )
{

// Add to histogram
if ( usecJitter < 1000 )
++m_nNegligible;
else if ( usecJitter < 2000 )
++m_n1;
else if ( usecJitter < 5000 )
++m_n2;
else if ( usecJitter < 10000 )
++m_n5;
else if ( usecJitter < 20000 )
++m_n10;
else
++m_n20;
}

inline int TotalCount() const
{
return m_nNegligible + m_n1 + m_n2 + m_n5 + m_n10 + m_n20;
}
};

/// Stats for the lifetime of a connection.
/// Should match CMsgSteamDatagramLinkLifetimeStats
struct SteamDatagramLinkLifetimeStats
Expand Down Expand Up @@ -87,30 +192,8 @@ struct SteamDatagramLinkLifetimeStats
int64 m_nMessagesRecvReliable;
int64 m_nMessagesRecvUnreliable;

//
// Ping distribution
//
int m_nPingHistogram25; // 0..25
int m_nPingHistogram50; // 26..50
int m_nPingHistogram75; // 51..75
int m_nPingHistogram100; // etc
int m_nPingHistogram125;
int m_nPingHistogram150;
int m_nPingHistogram200;
int m_nPingHistogram300;
int m_nPingHistogramMax; // >300
inline int PingHistogramTotalCount() const
{
return m_nPingHistogram25
+ m_nPingHistogram50
+ m_nPingHistogram75
+ m_nPingHistogram100
+ m_nPingHistogram125
+ m_nPingHistogram150
+ m_nPingHistogram200
+ m_nPingHistogram300
+ m_nPingHistogramMax;
}
PingHistogram m_pingHistogram;

// Distribution.
// NOTE: Some of these might be -1 if we didn't have enough data to make a meaningful estimate!
Expand All @@ -126,27 +209,7 @@ struct SteamDatagramLinkLifetimeStats
//
// Connection quality distribution
//
int m_nQualityHistogram100; // This means everything was perfect. If we delivered over 100 packets in the interval and were less than perfect, but greater than 99.5%, we will use 99% instead.
int m_nQualityHistogram99; // 99%+
int m_nQualityHistogram97;
int m_nQualityHistogram95;
int m_nQualityHistogram90;
int m_nQualityHistogram75;
int m_nQualityHistogram50;
int m_nQualityHistogram1;
int m_nQualityHistogramDead; // we received nothing during the interval; it looks like the connection dropped
inline int QualityHistogramTotalCount() const
{
return m_nQualityHistogram100
+ m_nQualityHistogram99
+ m_nQualityHistogram97
+ m_nQualityHistogram95
+ m_nQualityHistogram90
+ m_nQualityHistogram75
+ m_nQualityHistogram50
+ m_nQualityHistogram1
+ m_nQualityHistogramDead;
}
QualityHistogram m_qualityHistogram;

// Distribution. Some might be -1, see above for why.
short m_nQualityNtile2nd; // 2% of measurement intervals had quality <= N%
Expand All @@ -155,21 +218,7 @@ struct SteamDatagramLinkLifetimeStats
short m_nQualityNtile50th; // 50% of measurement intervals had quality <= N%

// Jitter histogram
int m_nJitterHistogramNegligible;
int m_nJitterHistogram1;
int m_nJitterHistogram2;
int m_nJitterHistogram5;
int m_nJitterHistogram10;
int m_nJitterHistogram20;
inline int JitterHistogramTotalCount() const
{
return m_nJitterHistogramNegligible
+ m_nJitterHistogram1
+ m_nJitterHistogram2
+ m_nJitterHistogram5
+ m_nJitterHistogram10
+ m_nJitterHistogram20;
}
JitterHistogram m_jitterHistogram;

//
// Connection transmit speed histogram
Expand Down
108 changes: 68 additions & 40 deletions src/steamnetworkingsockets/steamnetworking_statsutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,6 @@ struct PacketRate_t
/// Class used to track ping values
struct PingTracker
{
void Reset();

/// Called when we receive a ping measurement
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow );

struct Ping
{
Expand Down Expand Up @@ -151,6 +147,12 @@ struct PingTracker
/// Ping estimate, being optimistic
int OptimisticPingEstimate() const;

/// Estimate a conservative (i.e. err on the large side) timeout for the connection
SteamNetworkingMicroseconds CalcConservativeTimeout() const
{
return ( m_nSmoothedPing >= 0 ) ? ( PessimisticPingEstimate()*2000 + 250000 ) : k_nMillion;
}

/// Smoothed ping value
int m_nSmoothedPing;

Expand All @@ -159,24 +161,69 @@ struct PingTracker
/// a simple timestamp, or possibly because it will contain a sequence number, and
/// we will be able to look up that sequence number and remember when we sent it.)
SteamNetworkingMicroseconds m_usecTimeLastSentPingRequest;
protected:
void Reset();

/// Called when we receive a ping measurement
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow );
};

/// Ping tracker that tracks detailed lifetime stats
struct PingTrackerDetailed : PingTracker
{
void Reset()
{
PingTracker::Reset();
m_sample.Clear();
m_histogram.Reset();
}
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow )
{
PingTracker::ReceivedPing( nPingMS, usecNow );
m_sample.AddSample( std::min( nPingMS, 0xffff ) );
m_histogram.AddSample( nPingMS );
}

/// Total number of pings we have received
inline int TotalPingsReceived() const { return m_sample.NumSamplesTotal(); }

/// Should match CMsgSteamDatagramLinkLifetimeStats
int m_nHistogram25;
int m_nHistogram50;
int m_nHistogram75;
int m_nHistogram100;
int m_nHistogram125;
int m_nHistogram150;
int m_nHistogram200;
int m_nHistogram300;
int m_nHistogramMax;

/// Track sample of pings received so we can generate a histogram.
/// Track sample of pings received so we can generate percentiles.
/// Also tracks how many pings we have received total
PercentileGenerator<uint16> m_sample;

/// Counts by bucket
PingHistogram m_histogram;

/// Populate structure
void GetLifetimeStats( SteamDatagramLinkLifetimeStats &s ) const
{
s.m_pingHistogram = m_histogram;

s.m_nPingNtile5th = m_sample.NumSamples() < 20 ? -1 : m_sample.GetPercentile( .05f );
s.m_nPingNtile50th = m_sample.NumSamples() < 2 ? -1 : m_sample.GetPercentile( .50f );
s.m_nPingNtile75th = m_sample.NumSamples() < 4 ? -1 : m_sample.GetPercentile( .75f );
s.m_nPingNtile95th = m_sample.NumSamples() < 20 ? -1 : m_sample.GetPercentile( .95f );
s.m_nPingNtile98th = m_sample.NumSamples() < 50 ? -1 : m_sample.GetPercentile( .98f );
}
};

/// Ping tracker that only tracks totals
struct PingTrackerBasic : PingTracker
{
void Reset()
{
PingTracker::Reset();
m_nTotalPingsReceived = 0;
}
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow )
{
PingTracker::ReceivedPing( nPingMS, usecNow );
++m_nTotalPingsReceived;
}

inline int TotalPingsReceived() const { return m_nTotalPingsReceived; }

int m_nTotalPingsReceived;
};

/// Token bucket rate limiter
Expand Down Expand Up @@ -226,21 +273,17 @@ struct TokenBucketRateLimiter
float m_flTokenDeficitFromFull;
};


/// Class used to handle link quality calculations.
struct LinkStatsTrackerBase
{

/// Estimate a conservative (i.e. err on the large side) timeout for the connection
SteamNetworkingMicroseconds CalcConservativeTimeout() const
{
return ( m_ping.m_nSmoothedPing >= 0 ) ? ( m_ping.m_nSmoothedPing*2 + 500000 ) : k_nMillion;
}

/// What version is the peer running? It's 0 if we don't know yet.
uint32 m_nPeerProtocolVersion;

/// Ping
PingTracker m_ping;
PingTrackerDetailed m_ping;

//
// Outgoing stats
Expand Down Expand Up @@ -360,23 +403,10 @@ struct LinkStatsTrackerBase
PercentileGenerator<uint8> m_qualitySample;

/// Histogram of quality intervals
int m_nQualityHistogram100;
int m_nQualityHistogram99;
int m_nQualityHistogram97;
int m_nQualityHistogram95;
int m_nQualityHistogram90;
int m_nQualityHistogram75;
int m_nQualityHistogram50;
int m_nQualityHistogram1;
int m_nQualityHistogramDead;
QualityHistogram m_qualityHistogram;

// Histogram of incoming latency variance
int m_nJitterHistogramNegligible; // <1ms
int m_nJitterHistogram1; // 1--2ms
int m_nJitterHistogram2; // 2--5ms
int m_nJitterHistogram5; // 5--10ms
int m_nJitterHistogram10; // 10--20ms
int m_nJitterHistogram20; // 20ms or more
JitterHistogram m_jitterHistogram;

//
// Misc stats bookkeeping
Expand All @@ -391,7 +421,7 @@ struct LinkStatsTrackerBase
/// note of the outcome.
inline bool BReadyToSendTracerPing( SteamNetworkingMicroseconds usecNow ) const
{
return m_ping.m_usecTimeLastSentPingRequest + k_usecLinkStatsPingRequestInterval < usecNow;
return std::max( m_ping.m_usecTimeLastSentPingRequest, m_ping.TimeRecvMostRecentPing() ) + k_usecLinkStatsPingRequestInterval < usecNow;
}

/// Check if we appear to be timing out and need to send an "aggressive" ping, meaning send it right
Expand Down Expand Up @@ -744,8 +774,6 @@ struct LinkStatsTracker : public TLinkStatsTracker
{
TLinkStatsTracker::TrackSentMessageExpectingSeqNumAckInternal( usecNow, bAllowDelayedReply );
}

void RecvPktNumAckInternal( int64 nPktNum );
};


Expand Down
Loading

0 comments on commit 578261c

Please sign in to comment.