octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Performance of odeset


From: Rik
Subject: Performance of odeset
Date: Mon, 17 Oct 2016 10:08:57 -0700

10/17/16

Carlo,

I made a stab at improving the performance of odeset.  The code in question is:

-- Code --
  if (nargin == 0 && nargout == 0)
    print_options ();
  else
    p.parse (varargin{:});
    odestruct = p.Results;
    odestruct_extra = p.Unmatched;

    ## FIXME: For speed, shouldn't this merge of structures only occur
    ##        when there is something in odestruct_extra?
    ## FIXME: Should alphabetical order of fieldnames be maintained
    ##        by using sort?
    s1 = cellfun (@(x) ifelse (iscell (x), {x}, x),
                  struct2cell (odestruct),
                  "UniformOutput", false);

    s2 = cellfun (@(x) ifelse (iscell (x), {x}, x),
                  struct2cell (odestruct_extra),
                  "UniformOutput", false);

    C = [fieldnames(odestruct)       s1;
         fieldnames(odestruct_extra) s2];

    odestruct = struct (C'{:});
  endif
-- End Code --

I benchmarked the code in the else branch 50 times with the input

x = odeset ("RelTol", 1e-1);

Running times:
median = 2.44 ms
mean = 2.59 ms

Most of the time there will never be an option in odestruct_extra so it is
useless to merge an empty struct into it.  I modified the code to test that.

-- Code 2 --
  else
    p.parse (varargin{:});
    odestruct = p.Results;
    odestruct_extra = p.Unmatched;

    ## FIXME: For speed, shouldn't this merge of structures only occur
    ##        when there is something in odestruct_extra?
    ## FIXME: Should alphabetical order of fieldnames be maintained
    ##        by using sort?
    fields2 = fieldnames (odestruct_extra);
    if (! isempty (fields2))
      s1 = cellfun (@(x) ifelse (iscell (x), {x}, x),
                    struct2cell (odestruct),
                    "UniformOutput", false);

      s2 = cellfun (@(x) ifelse (iscell (x), {x}, x),
                    struct2cell (odestruct_extra),
                    "UniformOutput", false);

      C = [fieldnames(odestruct)       s1;
           fieldnames(odestruct_extra) s2];

      odestruct = struct (C'{:});
    endif
  endif
-- End Code 2 --

Using the same benchmark running 50 times.

Running times:
median = .0379 ms
mean = .0385 ms

That's a 64X speedup so is worth doing.

Next I tried running with an input which will force the code to be used.

x = odeset ("FooBar", 1e-1);

I ran that 50 times and the results are similar to the first instance since
the merger of two structs is happening.

Running times:

median = 2.41 ms
mean = 2.46 ms

For small N, loops are often quite fast.  Since the number of unknown ODE
solver options is unlikely to be very large, I tried re-coding the merging
of the structures to

-- Code 3 --
    fields2 = fieldnames (odestruct_extra);
    if (! isempty (fields2))
      for [ val, key ] = odestruct_extra
        odestruct.(key) = val;
      endfor
   endif
-- End Code 3 --

Running times:

median = .0501 ms
mean =  .0542 ms

Only a 48X improvement, but still worth doing.

I attached the patch to https://savannah.gnu.org/bugs/index.php?49364 for
your review.  Within the for loop seems a natural place to also issue a
warning for each unknown property that is used which is the subject of that
bug report.

--Rik




reply via email to

[Prev in Thread] Current Thread [Next in Thread]