Hi RJ,

I've been testing this patch since you sent it to see if it had a
performance impact. As far as I can tell, the increased CPU utilization
in the getControl() calls is negligible. Regardless, correctness IS
more important than speed, so please apply your patch to trunk. :)

Thanks,
Albert

On Wed, 2008-07-02 at 23:41 -0400, Russell Ryan wrote:
> Hey all, as discussed in my previous email, here's a patch to some of 
> the issues we've seen in ControlObject::getControl.
> 
> One improvement is moving from Q3PtrList to QHash, and using ConfigKey 
> as the key into this hashset. The hash function I picked for ConfigKey 
> is composed of QString's built in hash function and xor:
> 
> for hash(configkey), the hash value is  QStringHash(group) XOR 
> QStringHash(key).
> 
> Anyone with experience with hash functions, please tell me if that's 
> reasonable :)
> 
> 
> getControl used to be O(n) in the number of ControlObjects that exist, 
> and using the hash, we can now expect O(1) performance.
> 
> In this patch, I've gone for correctness over speed. The ControlObject 
> hash is protected with a mutex for all operations -- add, remove, and 
> lookup. We have too many mysterious bugs to allow race condition between 
> a lookup and an append or remove.
> 
> Also, in looking at usage patterns for getControl, given that it uses a 
> lock now, that means you should cache its output wherever possible. If 
> you are writing a class, instead of using getControl each time you need 
> the control, look it up when the class is instantiated and store it in a 
> member variable instead of doing :     getControl(somekey)->get()  each 
> time you want the value.
> 
> 
> Let me know if you have any comments. This should eliminate the race 
> conditions we were seeing the other night. If everyone thinks it looks 
> good I'll push it into trunk, and we can see if this helps any of our 
> open bugs. (hey, we can be optimistic right? :) )
> 
> 
> Cheers,
> RJ
> plain text document attachment (control_object_hash_patch)
> Index: mixxx/src/controlobject.cpp
> ===================================================================
> --- mixxx/src/controlobject.cpp       (revision 2120)
> +++ mixxx/src/controlobject.cpp       (working copy)
> @@ -17,13 +17,14 @@
>  
>  #include <qwidget.h>
>  #include <QtDebug>
> -//Added by qt3to4:
> -#include <Q3PtrList>
> +#include <QHash>
>  #include "controlobject.h"
>  #include "controlevent.h"
>  
>  // Static member variable definition
> -Q3PtrList<ControlObject> ControlObject::m_sqList;
> +QHash<ConfigKey,ControlObject*> ControlObject::m_sqCOHash;
> +QMutex ControlObject::m_sqCOHashMutex;
> +
>  QMutex ControlObject::m_sqQueueMutexMidi;
>  QMutex ControlObject::m_sqQueueMutexThread;
>  QMutex ControlObject::m_sqQueueMutexChanges;
> @@ -39,12 +40,16 @@
>  {
>      m_dValue = 0.;
>      m_Key = key;
> -    m_sqList.append(this);
> +    m_sqCOHashMutex.lock();
> +    m_sqCOHash.insert(key,this);
> +    m_sqCOHashMutex.unlock();
>  }
>  
>  ControlObject::~ControlObject()
>  {
> -    m_sqList.remove(this);
> +    m_sqCOHashMutex.lock();
> +    m_sqCOHash.remove(m_Key);
> +    m_sqCOHashMutex.unlock();
>  }
>  
>  bool ControlObject::connectControls(ConfigKey src, ConfigKey dest)
> @@ -104,16 +109,17 @@
>  
>  ControlObject * ControlObject::getControl(ConfigKey key)
>  {
> -    // qDebug() << "trying to get group" << key.group << "item" << key.item;
> +    //qDebug() << "ControlObject::getControl for (" << key.group << "," << 
> key.item << ")";
> +    m_sqCOHashMutex.lock();
> +    if(m_sqCOHash.contains(key)) {
> +        ControlObject *co = m_sqCOHash[key];
> +        m_sqCOHashMutex.unlock();
> +        return co;
> +    }
> +    m_sqCOHashMutex.unlock();
>  
> -    // Loop through the list of ConfigObjects to find one matching key
> -    ControlObject * c;
> -    for (c=m_sqList.first(); c; c=m_sqList.next())
> -    {
> -        if (c->getKey().group == key.group && c->getKey().item == key.item)
> -            return c;
> -    }
> -    return 0;
> +    qDebug() << "ControlObject::getControl returning NULL for (" << 
> key.group << "," << key.item << ")";
> +    return NULL;
>  }
>  
>  void ControlObject::queueFromThread(double dValue, ControlObjectThread * 
> pControlObjectThread)
> Index: mixxx/src/controlobject.h
> ===================================================================
> --- mixxx/src/controlobject.h (revision 2120)
> +++ mixxx/src/controlobject.h (working copy)
> @@ -124,8 +124,10 @@
>  private:
>      /** List of associated proxy objects */
>      Q3PtrList<ControlObjectThread> m_qProxyList;
> -    /** List of ControlObject instantiations */
> -    static Q3PtrList<ControlObject> m_sqList;
> +    /** Hash of ControlObject instantiations */
> +    static QHash<ConfigKey,ControlObject*> m_sqCOHash;
> +    /** Mutex guarding access to the ControlObject hash **/
> +    static QMutex m_sqCOHashMutex;
>      /** Mutex protecting access to the queues */
>      static QMutex m_sqQueueMutexMidi, m_sqQueueMutexThread, 
> m_sqQueueMutexChanges;
>      /** Queue holding control changes from MIDI */
> Index: mixxx/src/configobject.h
> ===================================================================
> --- mixxx/src/configobject.h  (revision 2120)
> +++ mixxx/src/configobject.h  (working copy)
> @@ -26,6 +26,7 @@
>  #include <qkeysequence.h>
>  #include <qdom.h>
>  #include <qmap.h>
> +#include <QHash>
>  
>  typedef enum {
>      MIDI_EMPTY            = 0,
> @@ -58,10 +59,20 @@
>  public:
>      ConfigKey();
>      ConfigKey(QString g, QString i);
> -
> +   
>      QString group, item;
>  };
>  
> +/* comparison function for ConfigKeys. Used by a QHash in ControlObject */
> +inline bool operator==(const ConfigKey &c1, const ConfigKey &c2) {
> +    return c1.group == c2.group && c1.item == c2.item;
> +}
> +
> +/* QHash hash function for ConfigKey objects. */
> +inline uint qHash(const ConfigKey &key) {
> +    return qHash(key.group) ^ qHash(key.item);
> +}
> +
>  /*
>    The value corresponding to a key. The basic value is a string, but can be
>    subclassed to more specific needs.
> -------------------------------------------------------------------------
> Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
> Studies have shown that voting for your favorite open source project,
> along with a healthy diet, reduces your potential for chronic lameness
> and boredom. Vote Now at http://www.sourceforge.net/community/cca08
> _______________________________________________ Mixxx-devel mailing list 
> [email protected] 
> https://lists.sourceforge.net/lists/listinfo/mixxx-devel


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08
_______________________________________________
Mixxx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mixxx-devel

Reply via email to