【问题标题】:MATLAB: creating a fast function that closes over a large variableMATLAB:创建一个关闭大变量的快速函数
【发布时间】:2013-11-19 23:16:21
【问题描述】:

我正在尝试创建一个函数,该函数存储一个非常大的变量以供每次调用该函数时使用。我有一个函数myfun(x,y),其中y 非常大,因此执行速度很慢,因为 MATLAB 是按值传递的。但是,我只在程序执行期间传递变量y 一次,创建一个闭包,然后将其传递给另一个函数以重复调用:

Y = create_big_matrix();
newfun = @(x) myfun(x,Y);
some_other_fun(newfun); % This calls newfun several times

我假设每次调用newfun 时,都会将Y 的存储值复制到myfun。这似乎非常低效。有没有更好的方法来实现newfun,这样Y 只被复制一次,当newfun 被创建时(或者当它被传递给some_other_fun 时)?

【问题讨论】:

  • 除了COW,您可以将数据包装在句柄类对象(具有引用语义)中。

标签: matlab memory-management closures


【解决方案1】:

MATLAB has copy-on-write mechanisms 在调用myfun(x,Y) 时阻止Y 的副本,除非它修改了输入。在这种情况下,我认为您无需担心性能问题,但我必须查看myfun 的代码并运行测试来验证。

引用this post on a MathWorks blog:

一些用户认为,由于 MATLAB 的行为就像是按值传递数据(而不是按引用传递),所以 MATLAB 在调用函数时总是会复制输入。这不一定是真的。

文章接着描述了 MATLAB 在识别变量何时被函数修改并尽可能避免复制的能力有限。另请参阅 this SO answer 总结写时复制期间的“幕后”操作as described in an old newsreader post

要检查是否正在发生复制,请使用format debug 检查调用函数中Y 的数据指针地址pr,并再次检查myfun 内部。如果相同,则不会发生复制。设置断点以单步执行并检查此指针。

【讨论】:

  • 这种行为的性质多年来也发生了变化,因此取决于版本。
  • @horchler 是的。 COW 一直以某种形式出现在since at least 2000,但我认为这些年来它只会变得更好。您是指不同 MATLAB 版本的任何特定特性吗?我认为最好的方法是让你的代码保持自然,牢记 MATLAB 优化可能会失败的情况。
  • 我说的是幕后,mex,JIT 也改变了一些东西的处理方式(你的第一个链接指出了这一点)。例如,JIT 增强功能可以更轻松地编写高效的代码,而不会生成从未使用过的副本。
  • @horchler 澄清一下,COW 机制似乎与 JIT 编译器功能没有直接关系,正如您明确指出的那样,它与循环加速和内联数据处理有关(如第二链接页面的一部分)。 COW 似乎是 MATLAB 内存管理器的基本功能,至少在 R2013b 中是这样(使用feature jit offformat debug 测试this procedure)。如我错了请纠正我。也许我只是没有看到链接 JIT 和 COW 的参考。 :)
  • MathWorks 通常将此称为 JIT/加速器,但我相信它们实际上是两个独立的东西(您可以使用 feature jit offfeature accel off 打开或关闭它们)。在我看来,JIT 部分与在运行时检测代码中的“热点”并将它们动态编译成中间字节码(通常更快)有关,这样它在下次遇到时立即执行解释语言的通常成本(想想循环内的语句)。
【解决方案2】:

由于Y 不是函数句柄的参数,它只被传递给函数句柄一次,即在它执行时——包括所有可能存在的写时复制优化。 因此,function_handle 在定义时就获得了自己的 Y 版本。 在 function_handle 执行期间,Y 根本不会传递给 function_handle。 您可以通过一个简单的示例来形象化:

>> r = rand();
% at this point the function_handle gets lazy copy of r, stored in its own workspace
>> f = @() r; 
>> r
r =
    0.6423
% what happens to the original r, doesn't matter to the function-handle:
>> clear r
>> f()
ans =
    0.6423

只有在原始 Y 被修改/删除时才应制作实际副本(删除时真正复制的地方可能是另一个问题)。

您可以使用functions 来检查Y 是否真的被复制到了function_handle 工作区,如上例(仅r 的值不同):

r =

Structure address = c601538 
m = 1
n = 1
pr = 4c834a60 
pi = 0
    0.4464
>> fcn = functions(f);
>> fcn.workspace{1}.r
ans =

Structure address = c6010c0 
m = 1
n = 1
pr = 4c834a60 
pi = 0
    0.4464

您可以看到,正如 chappjc 的回答中所讨论的,此时参数只是被延迟复制 - 因此不会创建完整副本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 2022-11-19
    • 2012-10-20
    相关资源
    最近更新 更多