如果 for 循环更快,arrayfun 有什么用?

计算科学 matlab 高性能计算 寻根
2021-12-22 07:04:53

这可能不是科学计算问题,而是更多 MATLAB 问题,如果是这种情况,请随时关闭或迁移问题。

求根问题在科学计算中经常遇到,形式多种多样;但在这种情况下,目标是使用算子分裂技术求解非线性时间相关 PDE。在不深入 PDE 问题的细节的情况下(因为它与我的主要问题无关),每个时间步都会执行以下几行:

S = arrayfun(@(x,y) fsolve(@(c)[x-c(1)-c(1)*c(2);y-c(1)*c(2)],rand(1,2),options),u,v, ... 
      'UniformOutput',false);

options 是设置 fsolve 参数的结构,u 和 v 是中等大小(~8k 或更大)的列向量,fsolve 返回一个向量(1 行 x 2 列),需要设置选项(用于 arrayfun)UniformOutput错误的。我可以用以下 for 循环替换这两行:

for i=1:length(u)
    S(i,:) = fsolve(@(c)[u(i)-c(1)-c(1)*c(2);v(i)-c(1)*c(2)],rand(1,2),options);
end

这段摘录的运行速度是上一段的两倍。如果 S 提前初始化并转置(因此它与 MATLAB 的列优先顺序对齐),则时间差异会更加显着。显然,这种行为是意料之中的,因为 arrayfun 只是“隐藏”(内部化)了 for 循环,而且额外的函数句柄会引入额外的开销。

在这种观察下,函数arrayfun 的正确用例是什么?如果 for 循环更快,为什么有人会使用 arrayfun?特别是当(可以说)for循环更容易阅读时?

注意:有一些细微差别。例如,作为 arrayfun 的输出的 S 是一个元胞数组,与我们从 for 循环方法获得的 n×2 矩阵 S 形成对比。所以假设我不关心输出,它也可以在计算后被转储。我对为什么有人会在 for 循环中使用 arrayfun 的原因更感兴趣,因为 arrayfun 慢了 2 倍。

编辑 1:Octave 为 arrayfun 的存在提供了以下理由:“这对于不接受数组参数的函数很有用。”。但接着是“如果函数确实接受数组参数,最好直接调用函数。”,因为它们也只内化了 for 循环。在文档的最后,他们说内置的 @plus 函数比匿名函数快 60% @(x,y) x+y这可能是因为@plus 的行为更像 C 宏,因此没有额外的函数调用开销。

1个回答

我认为它的主要用途是抽象以及随之而来的优势:

  • 在许多情况下都会出现将函数应用于数组的每个元素。将这种模式包装到一个函数中提高了抽象级别:您不需要循环思考,您的意图可能更清晰。如果您熟悉 Python,那么推导式以及内置map函数都可以做到这一点。NumPy 还对数组的每个元素应用一个函数。
  • 它允许 MathWorks 有效地实现这种模式。虽然目前arrayfun用 for 循环实现的,但将来可能会改变。如果 MathWorks 稍后决定更改实现,只要函数签名保持不变,就不会出现向后兼容性问题。
  • 它允许第三方重新实现它,因为它已经在Parallel Computing Toolbox中完成。你需要重写你的代码parfor来达到同样的效果。