Skip to content

Commit 578261c

Browse files
committed
Stats cleanup/refactor.
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.)
1 parent b3182a5 commit 578261c

File tree

3 files changed

+278
-301
lines changed

3 files changed

+278
-301
lines changed

src/steamnetworkingsockets/steamnetworking_stats.h

Lines changed: 108 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,111 @@ struct SteamDatagramLinkInstantaneousStats
6060
void Clear();
6161
};
6262

63+
/// Counts of ping times by bucket
64+
struct PingHistogram
65+
{
66+
int m_n25, m_n50, m_n75, m_n100, m_n125, m_n150, m_n200, m_n300, m_nMax;
67+
68+
void Reset() { memset( this, 0, sizeof(*this) ); }
69+
70+
void AddSample( int nPingMS )
71+
{
72+
73+
// Update histogram using hand-rolled sort-of-binary-search, optimized
74+
// for the expectation that most pings will be reasonable
75+
if ( nPingMS <= 100 )
76+
{
77+
if ( nPingMS <= 50 )
78+
{
79+
if ( nPingMS <= 25 )
80+
++m_n25;
81+
else
82+
++m_n50;
83+
}
84+
else
85+
{
86+
if ( nPingMS <= 75 )
87+
++m_n75;
88+
else
89+
++m_n100;
90+
}
91+
}
92+
else
93+
{
94+
if ( nPingMS <= 150 )
95+
{
96+
if ( nPingMS <= 125 )
97+
++m_n125;
98+
else
99+
++m_n150;
100+
}
101+
else
102+
{
103+
if ( nPingMS <= 200 )
104+
++m_n200;
105+
else if ( nPingMS <= 300 )
106+
++m_n300;
107+
else
108+
++m_nMax;
109+
}
110+
}
111+
}
112+
113+
inline int TotalCount() const
114+
{
115+
return m_n25 + m_n50 + m_n75 + m_n100 + m_n125 + m_n150 + m_n200 + m_n300 + m_nMax;
116+
}
117+
};
118+
119+
/// Count of quality measurement intervals by bucket
120+
struct QualityHistogram
121+
{
122+
int m_n100, m_n99, m_n97, m_n95, m_n90, m_n75, m_n50, m_n1, m_nDead;
123+
124+
void Reset() { memset( this, 0, sizeof(*this) ); }
125+
126+
inline int TotalCount() const
127+
{
128+
return m_n100 + m_n99 + m_n97 + m_n95 + m_n90 + m_n75 + m_n50 + m_n1 + m_nDead;
129+
}
130+
};
131+
132+
/// Counts of jitter values by bucket
133+
struct JitterHistogram
134+
{
135+
void Reset() { memset( this, 0, sizeof(*this) ); }
136+
137+
int m_nNegligible; // <1ms
138+
int m_n1; // 1--2ms
139+
int m_n2; // 2--5ms
140+
int m_n5; // 5--10ms
141+
int m_n10; // 10--20ms
142+
int m_n20; // 20ms or more
143+
144+
void AddSample( SteamNetworkingMicroseconds usecJitter )
145+
{
146+
147+
// Add to histogram
148+
if ( usecJitter < 1000 )
149+
++m_nNegligible;
150+
else if ( usecJitter < 2000 )
151+
++m_n1;
152+
else if ( usecJitter < 5000 )
153+
++m_n2;
154+
else if ( usecJitter < 10000 )
155+
++m_n5;
156+
else if ( usecJitter < 20000 )
157+
++m_n10;
158+
else
159+
++m_n20;
160+
}
161+
162+
inline int TotalCount() const
163+
{
164+
return m_nNegligible + m_n1 + m_n2 + m_n5 + m_n10 + m_n20;
165+
}
166+
};
167+
63168
/// Stats for the lifetime of a connection.
64169
/// Should match CMsgSteamDatagramLinkLifetimeStats
65170
struct SteamDatagramLinkLifetimeStats
@@ -87,30 +192,8 @@ struct SteamDatagramLinkLifetimeStats
87192
int64 m_nMessagesRecvReliable;
88193
int64 m_nMessagesRecvUnreliable;
89194

90-
//
91195
// Ping distribution
92-
//
93-
int m_nPingHistogram25; // 0..25
94-
int m_nPingHistogram50; // 26..50
95-
int m_nPingHistogram75; // 51..75
96-
int m_nPingHistogram100; // etc
97-
int m_nPingHistogram125;
98-
int m_nPingHistogram150;
99-
int m_nPingHistogram200;
100-
int m_nPingHistogram300;
101-
int m_nPingHistogramMax; // >300
102-
inline int PingHistogramTotalCount() const
103-
{
104-
return m_nPingHistogram25
105-
+ m_nPingHistogram50
106-
+ m_nPingHistogram75
107-
+ m_nPingHistogram100
108-
+ m_nPingHistogram125
109-
+ m_nPingHistogram150
110-
+ m_nPingHistogram200
111-
+ m_nPingHistogram300
112-
+ m_nPingHistogramMax;
113-
}
196+
PingHistogram m_pingHistogram;
114197

115198
// Distribution.
116199
// NOTE: Some of these might be -1 if we didn't have enough data to make a meaningful estimate!
@@ -126,27 +209,7 @@ struct SteamDatagramLinkLifetimeStats
126209
//
127210
// Connection quality distribution
128211
//
129-
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.
130-
int m_nQualityHistogram99; // 99%+
131-
int m_nQualityHistogram97;
132-
int m_nQualityHistogram95;
133-
int m_nQualityHistogram90;
134-
int m_nQualityHistogram75;
135-
int m_nQualityHistogram50;
136-
int m_nQualityHistogram1;
137-
int m_nQualityHistogramDead; // we received nothing during the interval; it looks like the connection dropped
138-
inline int QualityHistogramTotalCount() const
139-
{
140-
return m_nQualityHistogram100
141-
+ m_nQualityHistogram99
142-
+ m_nQualityHistogram97
143-
+ m_nQualityHistogram95
144-
+ m_nQualityHistogram90
145-
+ m_nQualityHistogram75
146-
+ m_nQualityHistogram50
147-
+ m_nQualityHistogram1
148-
+ m_nQualityHistogramDead;
149-
}
212+
QualityHistogram m_qualityHistogram;
150213

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

157220
// Jitter histogram
158-
int m_nJitterHistogramNegligible;
159-
int m_nJitterHistogram1;
160-
int m_nJitterHistogram2;
161-
int m_nJitterHistogram5;
162-
int m_nJitterHistogram10;
163-
int m_nJitterHistogram20;
164-
inline int JitterHistogramTotalCount() const
165-
{
166-
return m_nJitterHistogramNegligible
167-
+ m_nJitterHistogram1
168-
+ m_nJitterHistogram2
169-
+ m_nJitterHistogram5
170-
+ m_nJitterHistogram10
171-
+ m_nJitterHistogram20;
172-
}
221+
JitterHistogram m_jitterHistogram;
173222

174223
//
175224
// Connection transmit speed histogram

src/steamnetworkingsockets/steamnetworking_statsutils.h

Lines changed: 68 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,6 @@ struct PacketRate_t
119119
/// Class used to track ping values
120120
struct PingTracker
121121
{
122-
void Reset();
123-
124-
/// Called when we receive a ping measurement
125-
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow );
126122

127123
struct Ping
128124
{
@@ -151,6 +147,12 @@ struct PingTracker
151147
/// Ping estimate, being optimistic
152148
int OptimisticPingEstimate() const;
153149

150+
/// Estimate a conservative (i.e. err on the large side) timeout for the connection
151+
SteamNetworkingMicroseconds CalcConservativeTimeout() const
152+
{
153+
return ( m_nSmoothedPing >= 0 ) ? ( PessimisticPingEstimate()*2000 + 250000 ) : k_nMillion;
154+
}
155+
154156
/// Smoothed ping value
155157
int m_nSmoothedPing;
156158

@@ -159,24 +161,69 @@ struct PingTracker
159161
/// a simple timestamp, or possibly because it will contain a sequence number, and
160162
/// we will be able to look up that sequence number and remember when we sent it.)
161163
SteamNetworkingMicroseconds m_usecTimeLastSentPingRequest;
164+
protected:
165+
void Reset();
166+
167+
/// Called when we receive a ping measurement
168+
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow );
169+
};
170+
171+
/// Ping tracker that tracks detailed lifetime stats
172+
struct PingTrackerDetailed : PingTracker
173+
{
174+
void Reset()
175+
{
176+
PingTracker::Reset();
177+
m_sample.Clear();
178+
m_histogram.Reset();
179+
}
180+
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow )
181+
{
182+
PingTracker::ReceivedPing( nPingMS, usecNow );
183+
m_sample.AddSample( std::min( nPingMS, 0xffff ) );
184+
m_histogram.AddSample( nPingMS );
185+
}
162186

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

166-
/// Should match CMsgSteamDatagramLinkLifetimeStats
167-
int m_nHistogram25;
168-
int m_nHistogram50;
169-
int m_nHistogram75;
170-
int m_nHistogram100;
171-
int m_nHistogram125;
172-
int m_nHistogram150;
173-
int m_nHistogram200;
174-
int m_nHistogram300;
175-
int m_nHistogramMax;
176-
177-
/// Track sample of pings received so we can generate a histogram.
190+
/// Track sample of pings received so we can generate percentiles.
178191
/// Also tracks how many pings we have received total
179192
PercentileGenerator<uint16> m_sample;
193+
194+
/// Counts by bucket
195+
PingHistogram m_histogram;
196+
197+
/// Populate structure
198+
void GetLifetimeStats( SteamDatagramLinkLifetimeStats &s ) const
199+
{
200+
s.m_pingHistogram = m_histogram;
201+
202+
s.m_nPingNtile5th = m_sample.NumSamples() < 20 ? -1 : m_sample.GetPercentile( .05f );
203+
s.m_nPingNtile50th = m_sample.NumSamples() < 2 ? -1 : m_sample.GetPercentile( .50f );
204+
s.m_nPingNtile75th = m_sample.NumSamples() < 4 ? -1 : m_sample.GetPercentile( .75f );
205+
s.m_nPingNtile95th = m_sample.NumSamples() < 20 ? -1 : m_sample.GetPercentile( .95f );
206+
s.m_nPingNtile98th = m_sample.NumSamples() < 50 ? -1 : m_sample.GetPercentile( .98f );
207+
}
208+
};
209+
210+
/// Ping tracker that only tracks totals
211+
struct PingTrackerBasic : PingTracker
212+
{
213+
void Reset()
214+
{
215+
PingTracker::Reset();
216+
m_nTotalPingsReceived = 0;
217+
}
218+
void ReceivedPing( int nPingMS, SteamNetworkingMicroseconds usecNow )
219+
{
220+
PingTracker::ReceivedPing( nPingMS, usecNow );
221+
++m_nTotalPingsReceived;
222+
}
223+
224+
inline int TotalPingsReceived() const { return m_nTotalPingsReceived; }
225+
226+
int m_nTotalPingsReceived;
180227
};
181228

182229
/// Token bucket rate limiter
@@ -226,21 +273,17 @@ struct TokenBucketRateLimiter
226273
float m_flTokenDeficitFromFull;
227274
};
228275

276+
229277
/// Class used to handle link quality calculations.
230278
struct LinkStatsTrackerBase
231279
{
232280

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

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

242285
/// Ping
243-
PingTracker m_ping;
286+
PingTrackerDetailed m_ping;
244287

245288
//
246289
// Outgoing stats
@@ -360,23 +403,10 @@ struct LinkStatsTrackerBase
360403
PercentileGenerator<uint8> m_qualitySample;
361404

362405
/// Histogram of quality intervals
363-
int m_nQualityHistogram100;
364-
int m_nQualityHistogram99;
365-
int m_nQualityHistogram97;
366-
int m_nQualityHistogram95;
367-
int m_nQualityHistogram90;
368-
int m_nQualityHistogram75;
369-
int m_nQualityHistogram50;
370-
int m_nQualityHistogram1;
371-
int m_nQualityHistogramDead;
406+
QualityHistogram m_qualityHistogram;
372407

373408
// Histogram of incoming latency variance
374-
int m_nJitterHistogramNegligible; // <1ms
375-
int m_nJitterHistogram1; // 1--2ms
376-
int m_nJitterHistogram2; // 2--5ms
377-
int m_nJitterHistogram5; // 5--10ms
378-
int m_nJitterHistogram10; // 10--20ms
379-
int m_nJitterHistogram20; // 20ms or more
409+
JitterHistogram m_jitterHistogram;
380410

381411
//
382412
// Misc stats bookkeeping
@@ -391,7 +421,7 @@ struct LinkStatsTrackerBase
391421
/// note of the outcome.
392422
inline bool BReadyToSendTracerPing( SteamNetworkingMicroseconds usecNow ) const
393423
{
394-
return m_ping.m_usecTimeLastSentPingRequest + k_usecLinkStatsPingRequestInterval < usecNow;
424+
return std::max( m_ping.m_usecTimeLastSentPingRequest, m_ping.TimeRecvMostRecentPing() ) + k_usecLinkStatsPingRequestInterval < usecNow;
395425
}
396426

397427
/// Check if we appear to be timing out and need to send an "aggressive" ping, meaning send it right
@@ -744,8 +774,6 @@ struct LinkStatsTracker : public TLinkStatsTracker
744774
{
745775
TLinkStatsTracker::TrackSentMessageExpectingSeqNumAckInternal( usecNow, bAllowDelayedReply );
746776
}
747-
748-
void RecvPktNumAckInternal( int64 nPktNum );
749777
};
750778

751779

0 commit comments

Comments
 (0)