Search code examples
performancematlab

InputParser vs exist(...,'var') vs nargin performance


Is there any performance comparison among those three variants for input checking/default initializaion?

It would be useful a comparison on a recent version e.g. R2014b and and older one R2012b.

An example:

function foo(a,b)
  if nargin < 1, a = 1; end
  if nargin < 2, b = 2; end
end

versus

function foo(a,b)
  if exist('a','var'), a = 1; end
  if exist('b','var'), b = 2; end
end

versus 

function foo(varargin)
  p = inputParser;
  addOptional(p,'a',1)
  addOptional(p,'b',2)
  parse(p,varargin{:})
end

Using Amro's testing suite, on R2014b:

  func           nargs       time   
_________________    _____    __________
'foo_nargin'         0        2.3674e-05
'foo_exist'          0        3.1339e-05
'foo_inputparser'    0        9.6934e-05
'foo_nargin'         1        2.4437e-05
'foo_exist'          1        3.2157e-05
'foo_inputparser'    1         0.0001307
'foo_nargin'         2        2.3838e-05
'foo_exist'          2        3.0492e-05
'foo_inputparser'    2        0.00015775

Solution

  • Here is some code to test the three approaches:

    function t = testArgParsing()
        args = {1, 2};
        fcns = {
            @foo_nargin ;
            @foo_exist ;
            @foo_inputparser
        };
    
        % parameters sweep
        [f,k] = ndgrid(1:numel(fcns), 0:numel(args));
        f = f(:); k = k(:);
    
        % test combinations of functions and number of input args
        t = cell(numel(f), 3);
        for i=1:size(t,1)
            t{i,1} = func2str(fcns{f(i)});
            t{i,2} = k(i);
            t{i,3} = timeit(@() feval(fcns{f(i)}, args{1:k(i)}), 2);
        end
    
        % format results in table
        t = cell2table(t, 'VariableNames',{'func','nargs','time'});
    end
    
    function [aa,bb] = foo_nargin(a,b)
      if nargin < 1, a = 1; end
      if nargin < 2, b = 2; end
      aa = a;
      bb = b;
    end
    
    function [aa,bb] = foo_exist(a,b)
      if ~exist('a','var'), a = 1; end
      if ~exist('b','var'), b = 2; end
      aa = a;
      bb = b;
    end
    
    function [aa,bb] = foo_inputparser(varargin)
      p = inputParser;
      addOptional(p,'a',1);
      addOptional(p,'b',2);
      parse(p, varargin{:});
      aa = p.Results.a;
      bb = p.Results.b;
    end
    

    Here is what I get in R2014a on my machine:

    >> t = testArgParsing
    t = 
              func           nargs       time   
        _________________    _____    __________
        'foo_nargin'         0        3.4556e-05
        'foo_exist'          0        5.2901e-05
        'foo_inputparser'    0        0.00010254
        'foo_nargin'         1        2.5531e-05
        'foo_exist'          1        3.7105e-05
        'foo_inputparser'    1         0.0001263
        'foo_nargin'         2        2.4991e-05
        'foo_exist'          2        3.6772e-05
        'foo_inputparser'    2        0.00015148
    

    And a pretty plot to view the results:

    tt = unstack(t, 'time', 'func');
    names = tt.Properties.VariableNames(2:end);
    bar(tt{:,2:end}.')
    set(gca, 'XTick',1:numel(names), 'XTickLabel',names, 'YGrid','on')
    legend(num2str(tt{:,1}, 'nargin=%d'))
    ylabel('Time [sec]'), xlabel('Functions')
    

    report