Enhanced Observable/Observer classes

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Enhanced Observable/Observer classes

Chris Higgs
At the QuantLib conference last week there was some discussion of how the observable pattern can give performance issues when there are many updates and many observers, either directly or via cascading updates e.g. quotes->curves->instruments. I have created an enhanced observable pattern which allows the disabling/enabling of updates, with optional deferment of updates - this means that when updating is re-enabled, any updates which would have been sent whilst disabled are then dispatched. Only one update is then sent per observer. So you can disable updates, change all the quotes on the curve, enable updates, and the curve is notified just once. The code for this can be seen at  https://github.com/higgscc/quantlib/blob/master/QuantLib/ql/patterns/observable.hpp

I also replaced the std::set with boost::unordered_set in the pattern classes as the latter is supposed to be faster as is it based on a hash table, and this has indeed itself produced a significant speedup.

All changes are backwards compatible. The method is complimentary to another method which has been used of adding a silent update method to the SimpleQuote class which does not trigger updates, these later being done manually by updating the dependent curve. That method is likely to be faster still than this, but does require the user to manually understand and manage the dependencies between his objects.

I also produced a test program to demonstrate if there was a performance benefit. There was no benefit visible if there is a single layer of observers, indeed it was slightly slower if updates were deferred, and the overhead in maintaining the deferred update list outweighed the benefit from sending less updates.

However if there are then secondary observers there is a significant speedup. The following test program took 0.016 seconds to execute if updates are deferred, but over two seconds if they are made directly (and repeatedly). These numbers are with all tracing turned off and compiled out.

Chris Higgs

   
//---------------------------------------------------------------------------------------------------
// what follows is some code to test the observable/observer deferred processing
//---------------------------------------------------------------------------------------------------

class DummyObserver : public Observer, public Observable
{
    public:
        DummyObserver() {}
        void update()
    {
        QL_TRACE(this << " notified of update");
        notifyObservers();
    }       
};

int main(int argc, char* argv[])
{
    QL_TRACE_ENABLE;

    boost::timer timer;

    // set up an observable
    // for example a quote
    boost::shared_ptr<Observable> observable(new Observable());

    // set up two observers
    // for example two curves depending on the quote
    DummyObserver* obsPtr = new DummyObserver();
    boost::shared_ptr<Observable> observer(obsPtr);
    obsPtr->registerWith(observable);   

    DummyObserver* obsPtr2 = new DummyObserver();
    boost::shared_ptr<Observable> observer2(obsPtr2);
    obsPtr2->registerWith(observable);   
   
    // set up some secondary observers
    // for example simulating trades depending on curves
    for (int i=0; i<1000; i++)
    {
        DummyObserver* secondaryObserver = new DummyObserver();
        secondaryObserver->registerWith(observer);
        secondaryObserver->registerWith(observer2);
    }

    QL_TRACE ("deferring updates");
    ObservableSettings::instance().disableUpdates(true);

    for (int i=0; i<100000; i++)
        observable->notifyObservers();

    QL_TRACE ("re-enabling updates");
    ObservableSettings::instance().enableUpdates();

    std::cout << "processed in " << std::fixed << std::setprecision(6) << timer.elapsed() << std::endl;
}

 


------------------------------------------------------------------------------
DreamFactory - Open Source REST & JSON Services for HTML5 & Native Apps
OAuth, Users, Roles, SQL, NoSQL, BLOB Storage and External API Access
Free app hosting. Or install the open source package on any LAMP server.
Sign up and see examples for AngularJS, jQuery, Sencha Touch and Native!
http://pubads.g.doubleclick.net/gampad/clk?id=63469471&iu=/4140/ostg.clktrk
_______________________________________________
QuantLib-dev mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-dev
Reply | Threaded
Open this post in threaded view
|

Re: Enhanced Observable/Observer classes

Chris Higgs
I have committed a new version with improved performance to my github

Chris


On Monday, 18 November 2013, 12:00, Chris Higgs <[hidden email]> wrote:
At the QuantLib conference last week there was some discussion of how the observable pattern can give performance issues when there are many updates and many observers, either directly or via cascading updates e.g. quotes->curves->instruments. I have created an enhanced observable pattern which allows the disabling/enabling of updates, with optional deferment of updates - this means that when updating is re-enabled, any updates which would have been sent whilst disabled are then dispatched. Only one update is then sent per observer. So you can disable updates, change all the quotes on the curve, enable updates, and the curve is notified just once. The code for this can be seen at  https://github.com/higgscc/quantlib/blob/master/QuantLib/ql/patterns/observable.hpp

I also replaced the std::set with boost::unordered_set in the pattern classes as the latter is supposed to be faster as is it based on a hash table, and this has indeed itself produced a significant speedup.

All changes are backwards compatible. The method is complimentary to another method which has been used of adding a silent update method to the SimpleQuote class which does not trigger updates, these later being done manually by updating the dependent curve. That method is likely to be faster still than this, but does require the user to manually understand and manage the dependencies between his objects.

I also produced a test program to demonstrate if there was a performance benefit. There was no benefit visible if there is a single layer of observers, indeed it was slightly slower if updates were deferred, and the overhead in maintaining the deferred update list outweighed the benefit from sending less updates.

However if there are then secondary observers there is a significant speedup. The following test program took 0.016 seconds to execute if updates are deferred, but over two seconds if they are made directly (and repeatedly). These numbers are with all tracing turned off and compiled out.

Chris Higgs

   
//---------------------------------------------------------------------------------------------------
// what follows is some code to test the observable/observer deferred processing
//---------------------------------------------------------------------------------------------------

class DummyObserver : public Observer, public Observable
{
    public:
        DummyObserver() {}
        void update()
    {
        QL_TRACE(this << " notified of update");
        notifyObservers();
    }       
};

int main(int argc, char* argv[])
{
    QL_TRACE_ENABLE;

    boost::timer timer;

    // set up an observable
    // for example a quote
    boost::shared_ptr<Observable> observable(new Observable());

    // set up two observers
    // for example two curves depending on the quote
    DummyObserver* obsPtr = new DummyObserver();
    boost::shared_ptr<Observable> observer(obsPtr);
    obsPtr->registerWith(observable);   

    DummyObserver* obsPtr2 = new DummyObserver();
    boost::shared_ptr<Observable> observer2(obsPtr2);
    obsPtr2->registerWith(observable);   
   
    // set up some secondary observers
    // for example simulating trades depending on curves
    for (int i=0; i<1000; i++)
    {
        DummyObserver* secondaryObserver = new DummyObserver();
        secondaryObserver->registerWith(observer);
        secondaryObserver->registerWith(observer2);
    }

    QL_TRACE ("deferring updates");
    ObservableSettings::instance().disableUpdates(true);

    for (int i=0; i<100000; i++)
        observable->notifyObservers();

    QL_TRACE ("re-enabling updates");
    ObservableSettings::instance().enableUpdates();

    std::cout << "processed in " << std::fixed << std::setprecision(6) << timer.elapsed() << std::endl;
}

 


------------------------------------------------------------------------------
DreamFactory - Open Source REST & JSON Services for HTML5 & Native Apps
OAuth, Users, Roles, SQL, NoSQL, BLOB Storage and External API Access
Free app hosting. Or install the open source package on any LAMP server.
Sign up and see examples for AngularJS, jQuery, Sencha Touch and Native!
http://pubads.g.doubleclick.net/gampad/clk?id=63469471&iu=/4140/ostg.clktrk
_______________________________________________
QuantLib-dev mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-dev



------------------------------------------------------------------------------
Shape the Mobile Experience: Free Subscription
Software experts and developers: Be at the forefront of tech innovation.
Intel(R) Software Adrenaline delivers strategic insight and game-changing
conversations that shape the rapidly evolving mobile landscape. Sign up now.
http://pubads.g.doubleclick.net/gampad/clk?id=63431311&iu=/4140/ostg.clktrk
_______________________________________________
QuantLib-dev mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-dev