changeset 104:d8ad747eb907

* first crack at threading cut code
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 12 May 2009 17:57:41 +0000
parents ef22bed1626a
children abbc482aaad2
files plugins/AdaptiveSpectrogram.cpp plugins/AdaptiveSpectrogram.h qm-vamp-plugins.pro
diffstat 3 files changed, 242 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/AdaptiveSpectrogram.cpp	Tue May 12 12:01:23 2009 +0000
+++ b/plugins/AdaptiveSpectrogram.cpp	Tue May 12 17:57:41 2009 +0000
@@ -25,10 +25,13 @@
 
 //#define DEBUG_VERBOSE 1
 
+static const int cutThreadCount = 4;
+
 AdaptiveSpectrogram::AdaptiveSpectrogram(float inputSampleRate) :
     Plugin(inputSampleRate),
-    m_w(9),
-    m_n(2)
+    m_w(8),
+    m_n(3),
+    m_first(true)
 //    m_w(0),
 //    m_n(2)
 {
@@ -36,6 +39,12 @@
 
 AdaptiveSpectrogram::~AdaptiveSpectrogram()
 {
+    for (int i = 0; i < m_cutThreads.size(); ++i) {
+        m_cutThreads[i]->finish();
+        m_cutThreads[i]->wait();
+        delete m_cutThreads[i];
+    }
+    m_cutThreads.clear();
 }
 
 string
@@ -113,7 +122,7 @@
     desc.unit = "";
     desc.minValue = 1;
     desc.maxValue = 10;
-    desc.defaultValue = 3;
+    desc.defaultValue = 4;
     desc.isQuantized = true;
     desc.quantizeStep = 1;
     list.push_back(desc);
@@ -125,7 +134,7 @@
     desc2.unit = "";
     desc2.minValue = 1;
     desc2.maxValue = 14;
-    desc2.defaultValue = 10;
+    desc2.defaultValue = 9;
     desc2.isQuantized = true;
     desc2.quantizeStep = 1;
     // I am so lazy
@@ -242,6 +251,8 @@
     delete[] tmprout;
     delete[] tmpiout;
 
+    m_first = true;//!!!
+
     Cutting *cutting = cut(s, maxwid/2, 0, 0, maxwid/2);
 
 #ifdef DEBUG_VERBOSE
@@ -264,11 +275,13 @@
         fs[0].push_back(f);
     }
 
+//    std::cerr << "process returning!\n" << std::endl;
+
     return fs;
 }
 
 void
-AdaptiveSpectrogram::printCutting(Cutting *c, string pfx)
+AdaptiveSpectrogram::printCutting(Cutting *c, string pfx) const
 {
     if (c->first) {
         if (c->cut == Cutting::Horizontal) {
@@ -283,29 +296,100 @@
     }
 }
 
+void
+AdaptiveSpectrogram::getSubCuts(const Spectrograms &s,
+                                int res,
+                                int x, int y, int h,
+                                Cutting *&top, Cutting *&bottom,
+                                Cutting *&left, Cutting *&right) const
+{
+    m_first = false;
+    if (m_first) {//!!!
+
+        m_first = false;
+
+        if (m_cutThreads.empty()) {
+            for (int i = 0; i < 4; ++i) {
+//            for (int i = 0; i < 1; ++i) {
+                CutThread *t = new CutThread(this);
+                t->start();
+                m_cutThreads.push_back(t);
+            }
+//            sleep(1); //!!!
+        }
+
+//    int threadIndices[4];
+//    int found = 0;
+//    for (int i = 0; i < m_cutThreads.size(); ++i) {
+//        if (!m_cutThreads[i]->busy()) {
+//            threadIndices[found] = i;
+//            if (++found == 4) break;
+//        }
+//    }
+
+//    if (found == 4) {
+
+        // enough threads available; use them.  Need to avoid threads calling back on cut() in this class before we have made all of our threads busy (otherwise the recursive call is likely to claim threads further down our threadIndices before we do) -- hence m_threadMutex
+
+        //!!! no, thread mutex not a good way, need a claim() call on each thread or something
+
+//        m_threadMutex.lock();
+        m_cutThreads[0]->cut(s, res, x, y + h/2, h/2); // top
+        m_cutThreads[1]->cut(s, res, x, y, h/2); // bottom
+        m_cutThreads[2]->cut(s, res/2, 2 * x, y/2, h/2); // left
+        m_cutThreads[3]->cut(s, res/2, 2 * x + 1, y/2, h/2); // right
+
+//        std::cerr << "set up all four" << std::endl;
+
+        top    = m_cutThreads[0]->get();
+        bottom = m_cutThreads[1]->get();
+        left   = m_cutThreads[2]->get();
+        right  = m_cutThreads[3]->get();
+/*
+        bottom = cut(s, res, x, y, h/2);
+
+        // The "horizontal" division is a left/right split.  Splitting
+        // this way places us in resolution res/2, which has lower
+        // vertical resolution but higher horizontal resolution.  We
+        // need to double x accordingly.
+        
+        left   = cut(s, res/2, 2 * x, y/2, h/2);
+        right  = cut(s, res/2, 2 * x + 1, y/2, h/2);
+*/
+//        std::cerr << "got all four" << std::endl;
+
+    } else {
+
+        // unthreaded
+
+        // The "vertical" division is a top/bottom split.
+        // Splitting this way keeps us in the same resolution,
+        // but with two vertical subregions of height h/2.
+
+        top    = cut(s, res, x, y + h/2, h/2);
+        bottom = cut(s, res, x, y, h/2);
+
+        // The "horizontal" division is a left/right split.  Splitting
+        // this way places us in resolution res/2, which has lower
+        // vertical resolution but higher horizontal resolution.  We
+        // need to double x accordingly.
+        
+        left   = cut(s, res/2, 2 * x, y/2, h/2);
+        right  = cut(s, res/2, 2 * x + 1, y/2, h/2);
+    }
+}
+
 AdaptiveSpectrogram::Cutting *
 AdaptiveSpectrogram::cut(const Spectrograms &s,
                          int res,
-                         int x, int y, int h)
+                         int x, int y, int h) const
 {
 //    cerr << "res = " << res << ", x = " << x << ", y = " << y << ", h = " << h << endl;
 
     if (h > 1 && res > s.minres) {
 
-        // The "vertical" division is a top/bottom split.
-        // Splitting this way keeps us in the same resolution,
-        // but with two vertical subregions of height h/2.
-
-        Cutting *top    = cut(s, res, x, y + h/2, h/2);
-        Cutting *bottom = cut(s, res, x, y, h/2);
-
-        // The "horizontal" division is a left/right split.  Splitting
-        // this way places us in resolution res/2, which has lower
-        // vertical resolution but higher horizontal resolution.  We
-        // need to double x accordingly.
-
-        Cutting *left   = cut(s, res/2, 2 * x, y/2, h/2);
-        Cutting *right  = cut(s, res/2, 2 * x + 1, y/2, h/2);
+        Cutting *top = 0, *bottom = 0, *left = 0, *right = 0;
+        getSubCuts(s, res, x, y, h, top, bottom, left, right);
 
         double vcost = top->cost + bottom->cost;
         double hcost = left->cost + right->cost;
@@ -374,7 +458,7 @@
 AdaptiveSpectrogram::assemble(const Spectrograms &s,
                               const Cutting *cutting,
                               vector<vector<float> > &rmat,
-                              int x, int y, int w, int h)
+                              int x, int y, int w, int h) const
 {
     switch (cutting->cut) {
 
--- a/plugins/AdaptiveSpectrogram.h	Tue May 12 12:01:23 2009 +0000
+++ b/plugins/AdaptiveSpectrogram.h	Tue May 12 17:57:41 2009 +0000
@@ -14,6 +14,8 @@
 #include <cmath>
 #include <vector>
 
+#include "thread/Thread.h"
+
 class AdaptiveSpectrogram : public Vamp::Plugin
 {
 public:
@@ -50,11 +52,6 @@
     int m_w;
     int m_n;
 
-    inline double xlogx(double x) {
-        if (x == 0.0) return 0.0;
-        else return x * log(x);
-    }
-
     struct Spectrogram
     {
         int resolution;
@@ -113,20 +110,138 @@
         }
     };
 
-    double cost(const Spectrogram &s, int x, int y) {
+    class CutThread : public Thread
+    {
+    public:
+        CutThread(const AdaptiveSpectrogram *as) :
+            m_as(as),
+//            m_busy(false),
+//            m_computed(false),
+            m_result(0),
+            m_workToDoC("CutThread: work to do"),
+            m_workToDo(false),
+            m_workDoneC("CutThread: work done"),
+            m_workDone(false),
+            m_finishing(false)
+        { }
+        ~CutThread() { }
+
+        void cut(const Spectrograms &s, int res, int x, int y, int h) {
+            m_workToDoC.lock();
+//            std::cerr << "locked in main thread" << std::endl;
+            m_s = &s;
+            m_res = res;
+            m_x = x;
+            m_y = y;
+            m_h = h;
+//            m_busy = true;
+//            m_computed = false;
+            m_workToDo = true;
+            m_workDone = false;
+            m_workToDoC.signal();
+            m_workDoneC.lock();
+            m_workToDoC.unlock();
+        }
+
+        Cutting *get() {
+//            std::cerr << "about to wait within main thread" << std::endl;
+            // m_workDoneC must be locked from prior call to cut()
+            while (!m_workDone) m_workDoneC.wait();
+//            std::cerr << "waited within main thread" << std::endl;
+//            m_workDoneC.lock();
+//            while (!m_computed) {
+//                std::cerr << "waiting within main thread" << std::endl;
+//                m_condition.wait();
+//            }
+            Cutting *c = m_result;
+            m_result = 0;
+            m_workDoneC.unlock();
+            return c;
+        }
+/*
+        bool busy() {
+            return m_busy;
+        }
+*/
+        void finish() {
+            m_finishing = true;
+            m_workToDoC.signal();
+        }
+
+    protected:
+        virtual void run() {
+            m_workToDoC.lock();
+//            std::cerr << "locked within run function" << std::endl;
+            while (!m_finishing) {
+//                if (!m_busy) {
+//                    std::cerr << "waiting within run function" << std::endl;
+//                    m_condition.wait();
+//                }
+                    while (!m_workToDo && !m_finishing) m_workToDoC.wait();
+//                    std::cerr << "waited within run function" << std::endl;
+                if (m_finishing) {
+                    break;
+                }
+                if (m_workToDo) {
+//                    std::cerr << "cut thread " << this << ": calling cut" << std::endl;
+                    m_result = m_as->cut(*m_s, m_res, m_x, m_y, m_h);
+//                    std::cerr << "cut returning" << std::endl;
+//                    m_computed = true;
+//                    m_busy = false;
+                    m_workToDo = false;
+                    m_workDone = true;
+//                    std::cerr << "signalling completion from run function" << std::endl;
+                    m_workDoneC.signal();
+                }
+            }
+            m_workToDoC.unlock();
+        }
+
+        const AdaptiveSpectrogram *m_as;
+        const Spectrograms *m_s;
+        int m_res;
+        int m_x;
+        int m_y;
+        int m_h;
+//        bool m_busy;
+//        bool m_computed;
+        Cutting *m_result;
+        Condition m_workToDoC;
+        bool m_workToDo;
+        Condition m_workDoneC;
+        bool m_workDone;
+        bool m_finishing;
+    };
+    mutable std::vector<CutThread *> m_cutThreads;//!!! mutable blargh
+
+///!!!    Mutex m_threadMutex;
+mutable    bool m_first; //!!! gross
+
+    double xlogx(double x) const {
+        if (x == 0.0) return 0.0;
+        else return x * log(x);
+    }
+
+    double cost(const Spectrogram &s, int x, int y) const {
         return xlogx(s.data[x][y]);
     }
 
-    double value(const Spectrogram &s, int x, int y) {
+    double value(const Spectrogram &s, int x, int y) const {
         return s.data[x][y];
     }
 
-    Cutting *cut(const Spectrograms &, int res, int x, int y, int h);
+    Cutting *cut(const Spectrograms &, int res, int x, int y, int h) const;
 
-    void printCutting(Cutting *, std::string);
+    void getSubCuts(const Spectrograms &, int res, int x, int y, int h,
+                    Cutting *&top, Cutting *&bottom,
+                    Cutting *&left, Cutting *&right) const;
 
-    void assemble(const Spectrograms &, const Cutting *, std::vector<std::vector<float> > &, int x, int y, int w, int h);
-};
+    void printCutting(Cutting *, std::string) const;
+
+    void assemble(const Spectrograms &, const Cutting *,
+                  std::vector<std::vector<float> > &,
+                  int x, int y, int w, int h) const;
+    };
 
 
 #endif
--- a/qm-vamp-plugins.pro	Tue May 12 12:01:23 2009 +0000
+++ b/qm-vamp-plugins.pro	Tue May 12 17:57:41 2009 +0000
@@ -1,11 +1,16 @@
 
 TEMPLATE = lib
 
-CONFIG += plugin warn_on debug
+CONFIG += plugin warn_on release
 CONFIG -= qt
 
-linux-g++:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -fPIC -march=pentium3 -mfpmath=sse -msse
-linux-g++-64:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -fPIC -ffast-math -fno-exceptions
+linux-g++*:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -fno-exceptions -fPIC -ffast-math -msse -msse2 -ftree-vectorize -fomit-frame-pointer
+
+linux-g++*:DEFINES += USE_PTHREADS
+macx-g++*:DEFINES += USE_PTHREADS
+
+#linux-g++:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -fPIC -march=pentium3 -mfpmath=sse -msse
+#linux-g++-64:QMAKE_CXXFLAGS_RELEASE += -DNDEBUG -O3 -fPIC -ffast-math -fno-exceptions
 
 OBJECTS_DIR = tmp_obj
 MOC_DIR = tmp_moc
@@ -13,11 +18,11 @@
 INCLUDEPATH += ../vamp-plugin-sdk ../qm-dsp
 LIBPATH += ../vamp-plugin-sdk/vamp-sdk ../qm-dsp
 
-linux-g++:LIBS += -static-libgcc -Wl,-Bstatic -lqm-dsp -lvamp-sdk -L/usr/lib/sse2/atlas -L/usr/lib/atlas/sse -llapack -lblas $$system(g++ -print-file-name=libstdc++.a) -lc -Wl,-Bdynamic -Wl,--version-script=vamp-plugin.map
+linux-g++:LIBS += -static-libgcc -Wl,-Bstatic -lqm-dsp -lvamp-sdk -L/usr/lib/sse2/atlas -L/usr/lib/atlas/sse -llapack -lblas $$system(g++ -print-file-name=libstdc++.a) -lc -Wl,-Bdynamic -lpthread -Wl,--version-script=vamp-plugin.map
 
-linux-g++-64:LIBS += -Lbuild/linux/amd64  -Wl,-Bstatic -lqm-dsp -lvamp-sdk -llapack -lcblas -latlas -Wl,-Bdynamic -Wl,--version-script=vamp-plugin.map
+linux-g++-64:LIBS += -Lbuild/linux/amd64  -Wl,-Bstatic -lqm-dsp -lvamp-sdk -llapack -lcblas -latlas -Wl,-Bdynamic -lpthread -Wl,--version-script=vamp-plugin.map
 
-macx-g++:LIBS += -framework Accelerate -exported_symbols_list=vamp-plugin.list
+macx-g++:LIBS += -framework Accelerate -lpthread -exported_symbols_list=vamp-plugin.list
 
 #LIBS += -Wl,-Bstatic -lqm-dsp -lvamp-sdk -L/usr/lib/atlas/sse -lblas -llapack -Wl,-Bdynamic