matlabclass-constants

Performance of MATLAB's class constants


I have several helper functions in my code which get called many times for a given numerical calculation. Those helper functions use some constant values for their calculations. The same constant value might be used by more than one helper function.

This seems like an ideal scenario to define class properties with constant values. However, I have done some benchmark tests and I am quite surprised with the results.

Consider the following class for example (Consts.m):

classdef Consts
    properties (Constant)
        A = 0.5
        B = 3
    end

    properties
        VariableA
        VariableB
    end

    methods
        function obj = Consts()
            obj.VariableA = 0.5;
            obj.VariableB = 3;
        end
    end
end

And the following file (speed_tests.m):

function speed_tests()
    tic;
    for i = 1:200000
        direct_constant_access(1, 2);
    end
    fprintf('Direct constant access: ');
    toc;

    tic;
    c = Consts();
    for i = 1:200000
        passing_extra_obj(1, 2, c);
    end
    fprintf('Passing extra object: ');
    toc;

    tic;
    for i = 1:200000
        persistent_constants(1, 2);
    end
    fprintf('Persistent constants: ');
    toc;

    % Let's assume this code is executed at some point externally:
    % global A B;
    % A = 0.5;
    % B = 3;
    tic;
    for i = 1:200000
        defined_globally(1, 2);
    end
    fprintf('Defined globally: ');
    toc;

    tic;
    for i = 1:200000
        hardcoded(1, 2);
    end
    fprintf('Hardcoded: ');
    toc;

    tic;
    for i = 1:200000
        hardcoded_v2(1, 2);
    end
    fprintf('Hardcoded v2: ');
    toc;
end

function val = direct_constant_access(a, b)
    val = (a + Consts.A)^2 + log(b * Consts.B);
end

function val = passing_extra_obj(a, b, obj)
    val = (a + obj.VariableA)^2 + log(b * obj.VariableB);
end

function val = persistent_constants(a, b)
    persistent A B;
    if isempty(A)
        A = Consts.A^2;
        B = Consts.B;
    end
    val = (a + A)^2 + log(b * B);
end

function val = defined_globally(a, b)
    global A B;
    val = (a + A)^2 + log(b * B);
end

function val = hardcoded(a, b)
    val = (a + 0.5)^2 + log(b * 3);
end

function val = hardcoded_v2(a, b)
    A = 0.5;
    B = 3;
    val = (a + A)^2 + log(b * B);
end

When I run speed_tests() on MATLAB R2010b, this is what I obtain (your mileage may vary):

>> speed_tests()
Direct constant access: Elapsed time is 5.973690 seconds.
Passing extra object: Elapsed time is 1.760897 seconds.
Persistent constants: Elapsed time is 1.594263 seconds.
Defined globally: Elapsed time is 1.559441 seconds.
Hardcoded: Elapsed time is 0.673995 seconds.
Hardcoded v2: Elapsed time is 0.661189 seconds.

Perhaps I am too used to other programming languages (where true constants might simply get replaced by literals at compile time), but is accessing class constants really that slow in MATLAB or am I missing something?

When I try the same in MATLAB R2013a (same computer), this direct constant access seems to have improved quite a lot:

>> speed_tests()
Direct constant access: Elapsed time is 2.168146 seconds.
Passing extra object: Elapsed time is 1.593721 seconds.
Persistent constants: Elapsed time is 2.302785 seconds.
Defined globally: Elapsed time is 1.404252 seconds.
Hardcoded: Elapsed time is 0.531191 seconds.
Hardcoded v2: Elapsed time is 0.493668 seconds.

Still, none of the non-hardcoded versions is anywhere near the hardcoded ones. These are the only two MATLAB versions I have available at work, so I am unaware if this has kept improving over the years (and it wouldn't be relevant for myself, since I wouldn't be able to use newer versions anyway).

The CPU time is quite an important factor for what I am developing, but I would like to avoid filling the code with hardcoded literals if I can. Aren't class constants the alleged way of avoiding this?

Is there anything else that I could consider instead?

Note: the real helper functions get called with different arguments each time, so caching the result does not help in my case.


Solution

  • I've run into this issue as well, if there is a trick to reducing the overhead of accessing class objects I would also love to know.

    When I can I try to minimize the number of times the object is accessed. In your example I would access A and B once before starting the loop, and then pass them as arguments to each function call.

    function speed_tests()
        tic;
        A = Consts.A;
        B = Consts.B;
        for i = 1:200000
            passing_arguments(1, 2, A, B);
        end
        fprintf('Passing arguments: ');
        toc;
    
        tic;
        for i = 1:200000
            persistent_constants(1, 2);
        end
        fprintf('Persistent constants: ');
        toc;
    
        tic;
        for i = 1:200000
            hardcoded(1, 2);
        end
        fprintf('Hardcoded: ');
        toc;    
    end
    
    function val = passing_arguments(a, b, A, B)
        val = (a + A)^2 + log(b * B);
    end
    
    function val = persistent_constants(a, b)
        persistent A B;
        if isempty(A)
            A = Consts.A^2;
            B = Consts.B;
        end
        val = (a + A)^2 + log(b * B);
    end
    
    function val = hardcoded(a, b)
        val = (a + 0.5)^2 + log(b * 3);
    end
    

    Output:

    Passing arguments: Elapsed time is 0.035402 seconds.
    Persistent constants: Elapsed time is 0.208998 seconds.
    Hardcoded: Elapsed time is 0.027781 seconds.