Octave 无处不在的延迟复制传值语义对用户自定义的 subsasgn 方法的性能提出了一个问题。想象以下对 subsasgn 的调用:
ss = substruct ("()", {1});
x = subsasgn (x, ss, 1);
其中对应的方法如下所示:
function x = subsasgn (x, ss, val)
...
x.myfield (ss.subs{1}) = val;
endfunction
问题在于,进入 subsasgn 方法时,x 仍然被调用者作用域所引用。这意味着该方法在执行赋值之前,首先需要解除共享(即复制)x 和 x.myfield。调用完成后,除非发生错误,否则结果会立即赋值给调用者作用域中的 x,从而丢弃 x.myfield 之前的值。因此,Octave 语言意味着需要复制 N 个元素(N 为 x.myfield 的大小),而实际上仅修改一个元素就足够了。换句话说,原本的常数时间操作退化成了线性时间操作。对于本质上存储大型数组的用户类来说,这可能是一个真正的问题。
为了部分解决这个问题,Octave 对编写为 m 文件的用户自定义 subsasgn 方法使用了一种特殊的优化。当该方法通过内置的赋值语法(而非像上面那样的直接 subsasgn 调用)被调用时,即 x(1) = 1,并且如果 subsasgn 方法声明了相同的输入和输出参数(如上例所示),那么 Octave 将忽略在调用者作用域内复制 x 的过程。因此,在方法执行期间对 x 所做的任何更改也将直接影响调用者的副本。例如,这允许定义一个多项式类,其中修改单个元素只需常数时间。
了解这种优化带来的影响是很重要的。由于调用者作用域中不存在额外的 x 副本,因此如果执行期间发生错误,完全由被调用者负责不让 x 处于无效状态。此外,如果方法部分更改了 x 然后出错退出,这些更改将影响调用者作用域中的 x。然而,在 subsasgn 内部删除或完全替换 x 不会产生任何效果——只有索引赋值才有效。
由于这种优化可能会改变代码的工作方式(尤其是编写不佳的代码),因此提供了一个名为 optimize_subsasgn_calls 的函数来控制它。此功能默认是启用的。另一种避免此优化的方法是声明具有不同输出和输入参数的 subsasgn 方法,如下所示:
function y = subsasgn (x, ss, val) ... endfunction
版权所有 © 2024-2026 Octave中文网
ICP备案/许可证号:黑ICP备2024030411号-2