- Undocumented Matlab - https://undocumentedmatlab.com -

Class object creation performance

Posted By Yair Altman On December 11, 2013 | 8 Comments

Matlab’s Class Object System [1] (MCOS) is a powerful way to develop maintainable, modular, reusable code using a modern Object Oriented Programming (OOP) paradigm. Unfortunately, using OOP in Matlab carries some performance penalties that need to be considered when deciding whether to code in the new paradigm or keep using the older, simpler procedural paradigm. A major resource in this regard is a detailed post from 2012 [2] by Dave Foti, who heads MCOS development at MathWorks.
As Dave pointed out, the importance of MCOS’s overheads only comes into play when our program uses many class objects and calls many short-duration methods. In the majority of cases, this is actually not the case: Our performance bottlenecks are normally I/O, GUI, memory and processing algorithm – not object manipulation and function-call overheads. Unfortunately, Dave’s article left out many ideas for improving MCOS performance in those cases where it does matter.
Today’s article will expand on the sub-topic of MCOS object creation. While it does not directly rely on any undocumented aspect, I have also not seen it directly explained in the official docs. So consider this to be the missing doc page…

Constructor chaining

In his article, Dave’s main suggestion for improving object creation performance was to reduce the number of super-classes. When creating class objects, each creation needs to chain the default values and constructors of its ancestor super-classes. The more superclasses we have, the more modular our code can become, but this makes object creation slightly slower. The effect is typically on the order of a few millisecs or less, so unless we have a very long super-class chain or are bulk-creating numerous objects, this has little impact on the overall program performance.

Object pooling

Objects are typically created at the beginning of a program and used throughout it. In such cases, we only pay the small performance penalty once. In cases where objects are constantly being destroyed and created throughout the program’s duration, consider using object pooling [3] to reuse existing objects. The basic idea is to create a set of ready-to-use objects at the beginning of the program. A static (singleton [4]) dispatcher (factory [5]) object would create this pool of objects in its constructor, possibly with a few new objects ready for use. The factory’s only public interface would be public pull/recycle methods to retrieve and return objects from/to the pool. The program would have no access to the objects pool (except via these public methods), since the pool is stored in the factory object’s private data.
The pull method would create new objects only when asked to retrieve objects from an empty pool; otherwise it would simply return a reference to one of the unused pool objects (which need to be handle class objects [6] to be reference-able). The recycle method will be used to return objects to the pool, once they have been used, to enable these objects to be reused later (via subsequent pulls).
Object pooling entails programming overheads that only make sense when a large number of short-lived objects are constantly being created and destroyed, or when object creation is especially expensive. It is often used in databases (connection pooling), since programs often connect to a database numerous times for short-lived SQL queries. Similar ideas can be found in GUI and I/O programming.
Here is a simple implementation of such a system. The singleton factory class is Widgets and it holds a pool of reusable Widget objects:

% Manage a persistent, global, singleton list of Widget objects
classdef Widgets < handle
    properties (Access=private)
        UnusedWidgets@Widget   % only accept elements of type Widget
    end
    methods (Access=private)
        % Guard the constructor against external invocation.
        % We only want to allow a single instance of this class
        % This is ensured by calling the constructor from the static (non-class) getInstance() function
        function obj = Widgets()
            % Initialize an initial set of Widget objects
            for idx = 1 : 5
                try
                    obj.UnusedWidgets(idx) = Widget;
                catch
                    obj.UnusedWidgets = Widget;  % case of idx==1
                end
            end
        end
    end
    methods (Static)  % Access=public
        % Get a reference to an unused or new widget
        function widget = pull()
            obj = getInstance();
            try
                % Try to return a widget from the list of UnusedWidgets
                widget = obj.UnusedWidgets(end);
                obj.UnusedWidgets(end) = [];  % remove from list
            catch
                widget = Widget;  % create a new Widget object
            end
        end
        % Return a widget to the unused pool, once we are done with it
        function recycle(widget)
            obj = getInstance();
            obj.UnusedWidgets(end+1) = widget;
        end
    end
end
% Concrete singleton implementation
% Note: this is deliberately placed *outside* the class, so that it is not accessible to the user
function obj = getInstance()
    persistent uniqueInstance
    if isempty(uniqueInstance)
        obj = Widgets();
        uniqueInstance = obj;
    else
        obj = uniqueInstance;
    end
end

We can access and use the Widgets object pool as follows:

% Retrieve a Widget object instance from the pool of objects, or create a new instance as needed
widget = Widgets.pull();
% Now use this widget object until we're done with it
% Return the object to the pool, for possible later reuse
Widgets.recycle(widget);

Credit: inspired by Bobby Nedelkovski’s Singleton class implementation [7]

Handle vs. value class objects

Another consideration when designing classes is that while handle classes are slightly slower to create (due to multiple super-class overheads), they are typically much faster to use. The reason is that handle classes are passed to functions by reference, whereas value classes are passes by value. Whenever we modify a handle class property within a function, we directly manipulate the relevant property memory. On the other hand, when we manipulate a value class property, a copy of the class needs to be created and then the modified class needs to be copied back to the original object’s memory (using Matlab’s Copy-on-Write mechanism [8]). Since we cannot normally anticipate all usage patterns of a class when we create it, I suggest to create any new user class as handle class, unless there is a specific good reason to make it a value class. All it takes is to add the handle (or hgsetget) inheritance to the class definition:

classdef MyClass < handle

Preallocation

When implicit expansion of class-object arrays takes place, an abbreviated version of object instance creation [9] takes place, which bypasses the constructor calls and just copies the instance properties. For example, array(9)=Widget creates an array of 9 separate Widget objects, but the Widget constructor is only called for array(1) and array(9); array(1) is then expanded (copied-over) to the remaining objects array(2:8).
When preallocating, ensure that you are using the maximal expected array size. There is no point in preallocating an empty array or an array having a smaller size than the expected maximum, since dynamic memory reallocation will automatically kick-in within the processing-loop. For this reason, avoid using the empty() method [10] of class objects to preallocate - use repmat instead (ref [11]).
When using repmat to replicate class objects, always be careful to note whether you are replicating the object itself (this happens if your class does NOT derive from handle) or its reference handle (which happens if you derive the class from handle). If you are replicating objects, then you can safely edit any of their properties independently of each other; but if you replicate references, you are merely using multiple copies of the same reference, so that modifying referenced object #1 will also automatically affect all the other referenced objects. This may or may not be suitable for your particular program requirements, so be careful to check carefully. If you actually need to use independent object copies, you will need to call the class constructor multiple times, once for each new independent object (ref [12]).
Preallocation of class objects (class instances) can be used not just as a means of avoiding dynamic allocation, but also as a means of controlling the time of object initialization. Object initialization, typically done in the class’s constructor method, could be lengthy (depending on your specific application). By preallocating the object, we can control the exact timing in which this initialization occurs, possibly at such a time that is less time-critical in the regular application time-flow. This relates to the concepts of lazy initialization [13], a special case of deferred (or demand-driven) evaluation [14].
For additional aspects of preallocation performance, refer to my article from last year [15], which discusses class objects only briefly, but expands on the other data types.

London training course - March 2014

If you are interested in improving your Matlab application's performance and your Matlab programming skills in general, join my upcoming Advanced Matlab Programming course [16] in London, March 2014 – an intensive 2 day course on best practices, preparing professional reports and performance tuning. I guarantee that following this course your Matlab programming skills will be at a higher level.

Categories: Low risk of breaking in future versions, Stock Matlab function


8 Comments (Open | Close)

8 Comments To "Class object creation performance"

#1 Comment By Andrew On December 11, 2013 @ 11:39

Interesting use of The MathWorks calls “class-related” functions. Is there a reason that you didn’t make getInstance() an (Access=private,Static,Hidden) method instead? Also, is there a reason for not making Widgets a (Sealed) class to prevent subclassing?

#2 Comment By Yair Altman On December 11, 2013 @ 12:09

@Andrew – I could indeed make getInstance() Static+private (no need to make it hidden), but then I’d need to call it via the Widgets. prefix in pull()/recycle():

obj = Widgets.getInstance();

There’s also the small matter of performance – I believe that calling a sub-function as in my implementation is slightly faster than a class method invocation.

Not major reasons, I admit. There’s a lot of personal taste that goes into such implementations, and there are of course other good implementations. I’ve lost count of the number of singleton implementations I’ve seen over the years. It’s one of those classes that every newbie gets to program as an exercise, and doesn’t know that he got it wrong until he’s shown his/her errors in a technical job interview. Hopefully, my implementation doesn’t fall into this latter category…

As for not making Widgets Sealed, I see no reason to prevent sub-classes from reimplementing the pool differently. For example, instead of retrieving the last-recycled object, retrieving the oldest one in the pool.

#3 Comment By MatMan On December 12, 2013 @ 03:27

You could have mentioned that since Matlab 2011a (I think) you can copy handle classes without invoking the constructor: [23]

Instead of a pool of objects I store only one object and create as many copies as I need when I need them. I haven’t analyzed if a factory approach would be faster though…

#4 Comment By Yair Altman On December 12, 2013 @ 05:43

Thanks for mentioning this, matlab.mixin.Copyable can indeed be useful for fast shallow copies, in cases that do not require deep copies. And yes, it was indeed added in R2011a.

Readers interested in object copying might find the following discussion interesting: [24]

#5 Comment By MatMan On December 12, 2013 @ 07:24

A small addition:
matlab.mixin.Copyable can also be used for deep copies by overloading the copyElement method as described in the documentation.
Thanks for clearing up that when it was added!

#6 Comment By Matt Whitaker On December 12, 2013 @ 12:22

Hi Yair,
A note on the preallocation. If you have a parameter passed in on your class the automatically expanding classes will be called with a parameter constructor

So if your Widgets class had a varargin input for example then when you try

array(9)=Widgets('param');

will call the constructor passing ‘param’ in varargin{1} for the first constructor but then passes in an empty varargin for the next
So the moral I think is to have a no-parameter constructor defined if you are going to use this.

At least that’s the way it seems to work on my R2012b
Cheers

Matt

#7 Comment By Yair Altman On December 12, 2013 @ 12:24

@Matt – thanks for the clarification

#8 Pingback By Accessing private object properties | Undocumented Matlab On December 18, 2013 @ 11:16

[…] a copy of the original. Even if our original object is a handle class, the struct would still be a shallow copy and not a real reference to the object data.Mex’s standard mxGetProperty cannot be used on […]


Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/class-object-creation-performance

URLs in this post:

[1] Matlab’s Class Object System: http://www.mathworks.com/help/matlab/object-oriented-programming.html

[2] post from 2012: http://blogs.mathworks.com/loren/2012/03/26/considering-performance-in-object-oriented-matlab-code/

[3] object pooling: http://en.wikipedia.org/wiki/Object_pool_pattern

[4] singleton: http://en.wikipedia.org/wiki/Singleton_pattern

[5] factory: http://en.wikipedia.org/wiki/Factory_method_pattern

[6] handle class objects: http://www.mathworks.com/help/matlab/matlab_oop/comparing-handle-and-value-classes.html

[7] Singleton class implementation: http://www.mathworks.com/matlabcentral/fileexchange/24911-design-pattern-singleton-creational

[8] Matlab’s Copy-on-Write mechanism: http://undocumentedmatlab.com/blog/internal-matlab-memory-optimizations/

[9] abbreviated version of object instance creation: http://www.mathworks.com/help/matlab/matlab_oop/creating-object-arrays.html#bru6o00

[10] empty() method: http://www.mathworks.com/help/matlab/matlab_oop/creating-object-arrays.html#brd4nrh

[11] ref: http://stackoverflow.com/questions/2510427/how-to-preallocate-an-array-of-class-in-matlab

[12] ref: http://stackoverflow.com/questions/591495/matlab-preallocate-a-non-numeric-vector#591788

[13] lazy initialization: http://en.wikipedia.org/wiki/Lazy_initialization

[14] deferred (or demand-driven) evaluation: http://en.wikipedia.org/wiki/Lazy_evaluation

[15] article from last year: http://undocumentedmatlab.com/blog/preallocation-performance/

[16] Advanced Matlab Programming course: http://undocumentedmatlab.com/blog/sprintfc-undocumented-helper-function/#training

[17] Handle object as default class property value : https://undocumentedmatlab.com/articles/handle-object-as-default-class-property-value

[18] Class object tab completion & improper field names : https://undocumentedmatlab.com/articles/class-object-tab-completion-and-improper-field-names

[19] Creating a simple UDD class : https://undocumentedmatlab.com/articles/creating-a-simple-udd-class

[20] General-use object copy : https://undocumentedmatlab.com/articles/general-use-object-copy

[21] Performance: accessing handle properties : https://undocumentedmatlab.com/articles/performance-accessing-handle-properties

[22] Accessing private object properties : https://undocumentedmatlab.com/articles/accessing-private-object-properties

[23] : http://www.mathworks.com/help/matlab/ref/matlab.mixin.copyableclass.html

[24] : https://www.mathworks.com/matlabcentral/newsreader/view_thread/257925

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.