From 74973a93594e4150a827459708895577a795036b Mon Sep 17 00:00:00 2001
From: Mike Buland <mbuland@penny-arcade.com>
Date: Mon, 2 Jul 2018 11:34:44 -0700
Subject: Added a threadsafe speedometer class.

---
 src/tests/speedometer.cpp    |  51 ++++++++++++++++++
 src/unstable/speedometer.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++
 src/unstable/speedometer.h   |  43 ++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 src/tests/speedometer.cpp
 create mode 100644 src/unstable/speedometer.cpp
 create mode 100644 src/unstable/speedometer.h

(limited to 'src')

diff --git a/src/tests/speedometer.cpp b/src/tests/speedometer.cpp
new file mode 100644
index 0000000..4532f59
--- /dev/null
+++ b/src/tests/speedometer.cpp
@@ -0,0 +1,51 @@
+#include <bu/sio.h>
+#include <bu/speedometer.h>
+#include <bu/thread.h>
+
+#include <unistd.h>
+
+class Report : public Bu::Thread
+{
+public:
+    Report() :
+        sKeys( 500, 8 )
+    {
+    }
+
+    ~Report()
+    {
+    }
+
+    Bu::Speedometer sKeys;
+    bool bRunning;
+
+protected:
+    virtual void run()
+    {
+        bRunning = true;
+        while( bRunning )
+        {
+            usleep( 500000 );
+//            Bu::println("Speed: %1 - %2 k/s").arg( sKeys.getTotalEvents() ).
+//                arg( sKeys.getSpeed() );
+        }
+    }
+};
+
+int main( int argc, char *argv[] )
+{
+    Report r;
+
+    r.start();
+
+    for(;;)
+    {
+        getchar();
+        r.sKeys.addEvent();
+        r.sKeys.debug();
+    }
+
+
+    return 0;
+}
+
diff --git a/src/unstable/speedometer.cpp b/src/unstable/speedometer.cpp
new file mode 100644
index 0000000..75e424e
--- /dev/null
+++ b/src/unstable/speedometer.cpp
@@ -0,0 +1,120 @@
+#include "bu/speedometer.h"
+#include "bu/mutexlocker.h"
+#include "bu/sio.h"
+
+#include <sys/time.h>
+#include <string.h>
+
+Bu::Speedometer::Speedometer( int64_t iBucketMs, int32_t iBucketCount ) :
+    aiBucket( NULL ),
+    iTotal( 0 ),
+    iCapacity( iBucketCount ),
+    iFill( 0 ),
+    iStart( 0 ),
+    iCurrent( 0 ),
+    iBucketMs( iBucketMs ),
+    iStartTime( -1 ),
+    iWindowSizeMs( iBucketMs*iBucketCount ),
+    iLastUpdate( -1 )
+{
+    aiBucket = new uint32_t[iCapacity];
+    memset( aiBucket, 0, sizeof(uint32_t)*iCapacity );
+}
+
+Bu::Speedometer::~Speedometer()
+{
+    delete[] aiBucket;
+    aiBucket = NULL;
+}
+
+void Bu::Speedometer::addEvent()
+{
+    Bu::MutexLocker l( mLock );
+    int64_t iNow = getCurrentTime();
+    _updateBuckets( iNow );
+    if( iStartTime < 0 )
+    {
+        iStartTime = iNow;
+        iFill = 1;
+        aiBucket[iCurrent] = 1;
+        iTotal = 1;
+    }
+    else
+    {
+        aiBucket[iCurrent]++;
+        iTotal++;
+    }
+    iLastUpdate = iNow;
+}
+
+void Bu::Speedometer::debug()
+{
+    Bu::MutexLocker l( mLock );
+    Bu::print("%1 of %2: %3:%4: ").
+        arg( iFill ).arg( iCapacity ).arg( iStart ).arg( iCurrent );
+    for( int j = 0; j < iFill; j++ )
+    {
+        Bu::print("%1 | ").arg( aiBucket[(j+iStart)%iCapacity] );
+    }
+    Bu::println(" total = %1").arg( iTotal );
+}
+
+void Bu::Speedometer::_updateBuckets( int64_t iNow )
+{
+    if( iNow > iLastUpdate+iWindowSizeMs )
+    {
+        iLastUpdate = iStartTime = -1;
+        iFill = 0;
+        iStart = iCurrent = 0;
+        aiBucket[iCurrent] = 0;
+        iTotal = 0;
+    }
+    else 
+    {
+        int64_t iSkip = (iNow-(iStartTime+iBucketMs*iFill));
+        if( iSkip < 0 )
+            return;
+        iSkip = (iSkip/iBucketMs) + ((iSkip%iBucketMs)?(1):(0));
+
+        for( int64_t j = 0; j < iSkip; j++ )
+        {
+            iCurrent = (iCurrent+1)%iCapacity;
+            if( iFill < iCapacity )
+            {
+                iFill++;
+            }
+            else
+            {
+                iTotal -= aiBucket[iStart];
+                aiBucket[iStart] = 0;
+                iStart = (iStart+1)%iCapacity;
+                iStartTime += iBucketMs;
+            }
+        }
+    }
+}
+
+double Bu::Speedometer::getSpeed()
+{
+    Bu::MutexLocker l( mLock );
+    _updateBuckets( getCurrentTime() );
+
+    if( iStartTime < 0 )
+        return 0.0;
+
+    return (double)iTotal / (double)(iFill*iBucketMs);
+}
+
+int32_t Bu::Speedometer::getTotalEvents()
+{
+    Bu::MutexLocker l( mLock );
+    return iTotal;
+}
+
+int64_t Bu::Speedometer::getCurrentTime() const
+{
+    struct timeval tv;
+    gettimeofday( &tv, NULL );
+    return tv.tv_sec*1000 + tv.tv_usec/1000;
+}
+
diff --git a/src/unstable/speedometer.h b/src/unstable/speedometer.h
new file mode 100644
index 0000000..84d21ed
--- /dev/null
+++ b/src/unstable/speedometer.h
@@ -0,0 +1,43 @@
+#ifndef BU_SPEEDOMETER_H
+#define BU_SPEEDOMETER_H
+
+#include "bu/mutex.h"
+
+#include <stdint.h>
+
+namespace Bu
+{
+    class Speedometer
+    {
+    public:
+        Speedometer( int64_t iBucketMs, int32_t iBucketCount );
+        virtual ~Speedometer();
+
+        void addEvent();
+        double getSpeed();
+        int32_t getTotalEvents();
+        void debug();
+
+    private:
+        void _updateBuckets( int64_t iNow );
+        /**
+         * Gets the current time in milliseconds.
+         */
+        int64_t getCurrentTime() const;
+
+    private:
+        uint32_t *aiBucket;
+        uint32_t iTotal;
+        int32_t iCapacity;
+        int32_t iFill;
+        int32_t iStart;
+        int32_t iCurrent;
+        int64_t iBucketMs;
+        int64_t iStartTime;
+        int64_t iWindowSizeMs;
+        int64_t iLastUpdate;
+        mutable Bu::Mutex mLock;
+    };
+}
+
+#endif
-- 
cgit v1.2.3