Indexed Assignment Optimization (GNU Octave (version 6.2.92))

Previous: Defining Indexing And Indexed Assignment, 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 function `optimize_subsasgn_calls` is provided to control it. This feature is enabled 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: Defining Indexing And Indexed Assignment, Up: Indexing Objects [Contents][Index]