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 the following call to subsasgn

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

where 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. In other words, a constant-time operation is degraded 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 a direct subsasgn call as shown above), i.e., x(1) = 1, AND if the subsasgn method is declared with identical input and output arguments, as 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 will exist in the caller’s scope, it is solely the callee’s responsibility to not leave x in an invalid state if an error occurs during 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 way to avoid the optimization 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]