changeset 9602:dba091e1ee39

document subsasgn optimization in the OOP chapter
author Jaroslav Hajek <highegg@gmail.com>
date Wed, 02 Sep 2009 11:56:43 +0200
parents a9b37bae1802
children 8bea4e89326f
files doc/interpreter/oop.txi
diffstat 1 files changed, 78 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/oop.txi	Wed Sep 02 09:21:36 2009 +0200
+++ b/doc/interpreter/oop.txi	Wed Sep 02 11:56:43 2009 +0200
@@ -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