Welcome back.

This week, the first of a series of three posts on the implementation of design patterns in QuantLib.

Also, it’s still possible to register for my next course. For info, click on the links in the banner above. Unless you’re reading a month or two from now and the banner is no longer there. But in that case, the course would be done and there would be no point in clicking, so we’re good either way.

Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.

Design patterns

A few design patterns were implemented in QuantLib. You can refer to the Gang of Four book [1] for a description of such patterns; so why do I write about them? Well, as once noted by G. K. Chesterton,

[p]oets have been mysteriously silent on the subject of cheese

and the Gang was just as silent on a number of issues that come up when you write actual implementations—through no fault of them, mind you. The variations are almost limitless, and they were only four.

Thus, I will use these posts to point out a few ways in which our implementations were tailored to the requirements of the library.

The Observer pattern

The use of the Observer pattern in the QuantLib library is widespread; you’ve seen it used in past series of posts to let financial instruments and term structures keep track of changes and recalculate when needed.

Our version of the pattern (sketched in the next listing) is close enough to that described in the Gang of Four book; but as I mentioned, there are questions and problems that weren’t discussed there.

    class Observable {
        friend class Observer;
      public:
        void notifyObservers() {
            for (iterator i=observers_.begin();
                            i!=observers_.end(); ++i) {
                try {
                    (*i)->update();
                } catch (std::exception& e) {
                    // store information for later
                }
            }
        }
      private:
        void registerObserver(Observer* o) {
            observers_.insert(o);
        }
        void unregisterObserver(Observer*);
        list<Observer*> observers_;
    };

    class Observer {
      public:
        virtual ~Observer() {
            for (iterator i=observables_.begin();
                          i!=observables_.end(); ++i)
                (*i)->unregisterObserver(this);
        }
        void registerWith(const shared_ptr<Observable>& o) {
            o->registerObserver(this);
            observables_.insert(o);
        }
        void unregisterWith(const shared_ptr<Observable>&);
        virtual void update() = 0;
      private:
        list<shared_ptr<Observable> > observables_;
    };

For instance: what information should we include in the notification? In our implementation, we went for minimalism—all that an observer gets to know is that something changed. It would have been possible to provide more information (e.g., by having the update method take the notifying observable as an argument) so that observers could select what to recalculate and save a few cycles; but I don’t think that this feature was worth the added complexity.

Another question: what happens if an observer raises an exception from its update method? This would happen when an observable is sending a notification, i.e., while the observable is iterating over its observers, calling update on each one. If the exception were to propagate, the loop would be aborted and a number of observers would not receive the notification—bad. Our solution was to catch any such exception, complete the loop, and raise an exception at the end if anything went wrong. This causes the original exceptions to be lost, which is not good either; however, we felt this to be the lesser of the two evils.

Onwards to the third issue: that is, copy behavior. It is not very clear what should happen when an observer or an observable are copied. Currently, what seemed a sensible choice is implemented: on the one hand, copying an observable results in the copy not having any observer; on the other hand, copying an observer results in the copy being registered with the same observables as the original. However, other behaviors might be considered; as a matter of fact, the right choice might be to inhibit copying altogether.

The big problems, however, were two. First: we obviously had to make sure that the lifetimes of the observer and observables were managed properly, meaning that no notification should be sent to an already deleted object. To do so, we had observers store shared pointers to their observables, which ensures that no observable is deleted before an observer is done with it. The observers will unregister with any observable before being deleted, which in turn makes it safe for observables to store a list of raw pointers to their observers.

This, however, is only guaranteed to work in a single-threaded setting; and we are exporting QuantLib bindings to C# and Java, where unfortunately there is always another thread where the garbage collector is busy deleting stuff. Every once in a while, this caused random crashes as a notification was sent to a half-deleted object. Once the problem was understood, it was fixed (hi, Klaus) by means of an undocumented hook in the implementation of boost::shared_ptr; I’m not sure how this will work out when we switch to std::shared_ptr in the future. Also, the fix slows down the code, so it’s inactive by default and can be enabled by a compilation switch. Use it if you need the C# or Java bindings.

The second big problem is (notice that I didn’t say was) that, like in the Jerry Lee Lewis’ song, there’s a whole lotta notifyin’ going on. A change of date can easily trigger tens or hundreds of notifications; and even if most of the update methods only set a flag and forward the call, the time adds up.

People using QuantLib in applications where calculation time is paramount, such as CVA/XVA (hi, Peter) have worked around the problem by disabling notifications and recalculating explicitly. A step towards reducing notification time would be to remove the middlemen, and shorten the chains of notifications; however, this is not possible due to the ubiquitous presence of the Handle class in the chains. Handles can be relinked, and thus chains of dependencies can change even after objects are built.

In short, the problem is still not solved. You know where to find us if you have any bright ideas.

Bibliography

[1] E. Gamma, R. Helm, R. Johnson and J. Vlissides, Design Patterns: Element of Reusable Object-Oriented Software. Addison-Wesley, 1995.