Previous: , Up: Indexing Objects   [Contents][Index]


34.3.2 Indexed Assignment Optimization

Octave’s ubiquitous lazily-copied pass-by-value semantics implies a problem for performance of user-defined subsasgn methods. Imagine a call to subsasgn:

  ss = substruct ("()",{1});
  x = subsasgn (x, ss, 1);

and the corresponding method looking like this:

  function x = subsasgn (x, ss, val)
    …
    x.myfield (ss.subs{1}) = val;
  endfunction

The problem is that on entry to the subsasgn method, x is still referenced from the caller’s scope, which means that the method will first need to unshare (copy) x and x.myfield before performing the assignment. Upon completing the call, unless an error occurs, the result is immediately assigned to x in the caller’s scope, so that the previous value of x.myfield is forgotten. Hence, the Octave language implies a copy of N elements (N being the size of x.myfield), where modifying just a single element would actually suffice, i.e., degrades a constant-time operation to linear-time one. This may be a real problem for user classes that intrinsically store large arrays.

To partially solve the problem, Octave uses a special optimization for user-defined subsasgn methods coded as m-files. When the method gets called as a result of the built-in assignment syntax (not direct subsasgn call as shown above), i.e.

  x(1) = 1;

AND if the subsasgn method is declared with identical input and output argument, like in the example above, then Octave will ignore the copy of x inside the caller’s scope; therefore, any changes made to x during the method execution will directly affect the caller’s copy as well. This allows, for instance, defining a polynomial class where modifying a single element takes constant time.

It is important to understand the implications that this optimization brings. Since no extra copy of x in the caller’s scope will exist, it is solely the callee’s responsibility to not leave x in an invalid state if an error occurs throughout the execution. Also, if the method partially changes x and then errors out, the changes will affect x in the caller’s scope. Deleting or completely replacing x inside subsasgn will not do anything, however, only indexed assignments matter.

Since this optimization may change the way code works (especially if badly written), a built-in variable optimize_subsasgn_calls is provided to control it. It is on by default. Another option to avoid the effect is to declare subsasgn methods with different output and input arguments, like this:

  function y = subsasgn (x, ss, val)
    …
  endfunction

Previous: , Up: Indexing Objects   [Contents][Index]