From ce212041512aae9e2290b74da5746c8b2295166c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jul 2015 15:09:30 -0700 Subject: [PATCH] add output of connection stats in UDTTest --- libraries/networking/src/udt/Connection.cpp | 25 ++++++++++++++-- libraries/networking/src/udt/Connection.h | 2 ++ .../networking/src/udt/ConnectionStats.cpp | 30 ++++++++++++++++++- .../networking/src/udt/ConnectionStats.h | 15 +++++++++- libraries/networking/src/udt/Socket.cpp | 17 +++++++++++ libraries/networking/src/udt/Socket.h | 2 ++ tools/udt-test/src/UDTTest.cpp | 11 +++++++ tools/udt-test/src/UDTTest.h | 1 + 8 files changed, 98 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index b6b3f9f12a..71141656ae 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -126,9 +126,17 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) { ackPacket->writePrimitive((int32_t) udt::CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS); if (wasCausedBySyncTimeout) { + // grab the up to date packet receive speed and estimated bandwidth + int32_t packetReceiveSpeed = _receiveWindow.getPacketReceiveSpeed(); + int32_t estimatedBandwidth = _receiveWindow.getEstimatedBandwidth(); + + // update those values in our connection stats + _stats.recordReceiveRate(packetReceiveSpeed); + _stats.recordEstimatedBandwidth(estimatedBandwidth); + // pack in the receive speed and estimatedBandwidth - ackPacket->writePrimitive(_receiveWindow.getPacketReceiveSpeed()); - ackPacket->writePrimitive(_receiveWindow.getEstimatedBandwidth()); + ackPacket->writePrimitive(packetReceiveSpeed); + ackPacket->writePrimitive(estimatedBandwidth); // record this as the last ACK send time lastACKSendTime = high_resolution_clock::now(); @@ -261,6 +269,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber) { _nakInterval = (_rtt + 4 * _rttVariance); int receivedPacketsPerSecond = _receiveWindow.getPacketReceiveSpeed(); + if (receivedPacketsPerSecond > 0) { // the NAK interval is at least the _minNAKInterval // but might be the time required for all lost packets to be retransmitted @@ -371,10 +380,12 @@ void Connection::processACK(std::unique_ptr controlPacket) { // ACK the send queue so it knows what was received _sendQueue->ack(ack); - // update the RTT updateRTT(rtt); + // write this RTT to stats + _stats.recordRTT(rtt); + // set the RTT for congestion control _congestionControl->setRTT(_rtt); @@ -387,6 +398,10 @@ void Connection::processACK(std::unique_ptr controlPacket) { // these are calculated using an EWMA static const int EMWA_ALPHA_NUMERATOR = 8; + // record these samples in connection stats + _stats.recordReceiveRate(receiveRate); + _stats.recordEstimatedBandwidth(bandwidth); + _deliveryRate = (_deliveryRate * (EMWA_ALPHA_NUMERATOR - 1) + _deliveryRate) / EMWA_ALPHA_NUMERATOR; _bandwidth = (_bandwidth * (EMWA_ALPHA_NUMERATOR - 1) + _bandwidth) / EMWA_ALPHA_NUMERATOR; @@ -515,4 +530,8 @@ void Connection::updateCongestionControlAndSendQueue(std::function cong // now that we've update the congestion control, update the packet send period and flow window size _sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod); _sendQueue->setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize)); + + // record connection stats + _stats.recordPacketSendPeriod(_congestionControl->_packetSendPeriod); + _stats.recordCongestionWindowSize(_congestionControl->_congestionWindowSize); } diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 71cae880d0..83afb88db8 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -46,6 +46,8 @@ public: bool processReceivedSequenceNumber(SequenceNumber sequenceNumber); // returns indicates if this packet was a duplicate void processControl(std::unique_ptr controlPacket); + + ConnectionStats::Stats sampleStats() { return _stats.sample(); } signals: void packetSent(); diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp index 394448afec..b46eb3ee34 100644 --- a/libraries/networking/src/udt/ConnectionStats.cpp +++ b/libraries/networking/src/udt/ConnectionStats.cpp @@ -89,4 +89,32 @@ void ConnectionStats::recordSentPackets() { void ConnectionStats::recordReceivedPackets() { ++_currentSample.recievedPackets; ++_total.recievedPackets; -} \ No newline at end of file +} + +static const double EWMA_CURRENT_SAMPLE_WEIGHT = 0.125; +static const double EWMA_PREVIOUS_SAMPLES_WEIGHT = 1 - 0.125; + +void ConnectionStats::recordReceiveRate(int sample) { + _currentSample.receiveRate = sample; + _total.receiveRate = (_total.receiveRate * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT); +} + +void ConnectionStats::recordEstimatedBandwidth(int sample) { + _currentSample.estimatedBandwith = sample; + _total.estimatedBandwith = (_total.estimatedBandwith * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT); +} + +void ConnectionStats::recordRTT(int sample) { + _currentSample.rtt = sample; + _total.rtt = (_total.rtt * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT); +} + +void ConnectionStats::recordCongestionWindowSize(int sample) { + _currentSample.congestionWindowSize = sample; + _total.congestionWindowSize = (_total.congestionWindowSize * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT); +} + +void ConnectionStats::recordPacketSendPeriod(int sample) { + _currentSample.packetSendPeriod = sample; + _total.packetSendPeriod = (_total.packetSendPeriod * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT); +} diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h index df7dfd1545..fed1158bf7 100644 --- a/libraries/networking/src/udt/ConnectionStats.h +++ b/libraries/networking/src/udt/ConnectionStats.h @@ -36,6 +36,13 @@ public: int sentPackets { 0 }; int recievedPackets { 0 }; + + // the following stats are trailing averages in the result, not totals + int receiveRate { 0 }; + int estimatedBandwith { 0 }; + int rtt { 0 }; + int congestionWindowSize { 0 }; + int packetSendPeriod { 0 }; }; ConnectionStats(); @@ -57,6 +64,12 @@ public: void recordSentPackets(); void recordReceivedPackets(); + void recordReceiveRate(int sample); + void recordEstimatedBandwidth(int sample); + void recordRTT(int sample); + void recordCongestionWindowSize(int sample); + void recordPacketSendPeriod(int sample); + private: Stats _currentSample; Stats _total; @@ -64,4 +77,4 @@ private: } -#endif // hifi_ConnectionStats_h \ No newline at end of file +#endif // hifi_ConnectionStats_h diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 99cdb06b29..d1e953d650 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -213,3 +213,20 @@ void Socket::setCongestionControlFactory(std::unique_ptrsynInterval(); } + +void Socket::sampleAndPrintConnectionStats() { + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "sampleAndPrintConnectionStats"); + return; + } + + for(auto& connection : _connectionsHash) { + ConnectionStats::Stats sampleStats = connection.second->sampleStats(); + + qDebug() << connection.first + << sampleStats.receiveRate << sampleStats.rtt + << sampleStats.congestionWindowSize << sampleStats.packetSendPeriod + << sampleStats.sentPackets + << sampleStats.receivedACKs << sampleStats.receivedLightACKs << sampleStats.receivedNAKs; + } +} diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index 4f947e4043..02aa733e93 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -63,6 +63,8 @@ public: void setCongestionControlFactory(std::unique_ptr ccFactory); void connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* receiver, const char* slot); + + void sampleAndPrintConnectionStats(); private slots: void readPendingDatagrams(); diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index 9f149dce52..55a8a78f6f 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -116,6 +116,13 @@ UDTTest::UDTTest(int& argc, char** argv) : if (!_target.isNull()) { sendInitialPackets(); + + // the sender reports stats every 1 second + static const int STATS_SAMPLE_INTERVAL = 1000; + + QTimer* statsTimer = new QTimer(this); + connect(statsTimer, &QTimer::timeout, this, &UDTTest::sampleStats); + statsTimer->start(STATS_SAMPLE_INTERVAL); } } @@ -198,3 +205,7 @@ void UDTTest::sendPacket() { ++_totalQueuedPackets; } + +void UDTTest::sampleStats() { + _socket.sampleAndPrintConnectionStats(); +} diff --git a/tools/udt-test/src/UDTTest.h b/tools/udt-test/src/UDTTest.h index 050c5dc75a..1fd1836cf9 100644 --- a/tools/udt-test/src/UDTTest.h +++ b/tools/udt-test/src/UDTTest.h @@ -27,6 +27,7 @@ public: public slots: void refillPacket() { sendPacket(); } // adds a new packet to the queue when we are told one is sent + void sampleStats(); private: void parseArguments();