# HG changeset patch # User Jaroslav Hajek # Date 1251885403 -7200 # Node ID dba091e1ee39b5feb4e6fd9f3d7c665fb1b89a29 # Parent a9b37bae1802f46027e6fa3e79957594b79371c6 document subsasgn optimization in the OOP chapter diff --git a/doc/interpreter/oop.txi b/doc/interpreter/oop.txi --- a/doc/interpreter/oop.txi +++ b/doc/interpreter/oop.txi @@ -1,4 +1,5 @@ @c Copyright (C) 2008, 2009 David Bateman +@c Copyright (C) 2009 VZLU Prague @c @c This file is part of Octave. @c @@ -252,6 +253,14 @@ @node Indexing Objects @section Indexing Objects +@menu +* Defining Indexing And Indexed Assignment:: +* Indexed Assignment Optimization:: +@end menu + +@node Defining Indexing And Indexed Assignment +@subsection Defining Indexing And Indexed Assignment + Objects can be indexed with parentheses, either like @code{@var{a} (@var{idx})} or like @code{@var{a} @{@var{idx}@}}, or even like @code{@var{a} (@var{idx}).@var{field}}. However, it is up to the user @@ -312,6 +321,75 @@ @DOCSTRING(colon) +@node Indexed Assignment Optimization +@subsection 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: + +@example + ss = substruct ("()",@{1@}); + x = subsasgn (x, ss, 1); +@end example + +and the corresponding method looking like this: + +@example + function x = subsasgn (x, ss, val) + @dots{} + x.myfield(ss.subs@{1@}) = val; + endfunction +@end example + +The problem is that on entry to the subsasgn method, @code{x} is still +referenced from the caller's scope, which means that the method will +first need to unshare (copy) @code{x} and @code{x.myfield} before performing +the assignment. Upon completing the call, unless an error occurs, +the result is immediately assigned to @code{x} in the caller's scope, +so that the previous value of @code{x.myfield} is forgotten. Hence, the +Octave language implies a copy of N elements (N being the size of +@code{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. + +@example + x(1) = 1; +@end example + +@b{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 @code{x} inside +the caller's scope; therefore, any changes made to @code{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 @code{x} in the caller's scope will exist, it is @i{solely} +the callee's responsibility to not leave @code{x} in an invalid state if an error +occurs throughout the execution. Also, if the method partially changes @code{x} +and then errors out, the changes @i{will} affect @code{x} in the caller's scope. +Deleting or completely replacing @code{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 @code{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: + +@example + function y = subsasgn (x, ss, val) + @dots{} + endfunction +@end example + @node Overloading Objects @section Overloading Objects