在matlab中传递数组,没有开销(最好是“引用”),以避免复制复杂的代码块?

时间:2012-10-25 22:32:25

标签: matlab

我在Matlab脚本中有复杂的代码块,它们作用于大型非稀疏数组。该代码对数组中的随机元素以及读取操作执行许多写操作。相同的代码必须针对不同的(大型)数组执行(即,相同的代码块,不同的数组变量名称除外)。

我不希望只有数组名称不同的长重复代码块。

不幸的是,当我创建一个函数来执行操作时,代码块只出现一次,性能会降低10倍或更多(可能是由于复制了数组)。但是,我不需要复制数组。我宁愿“通过引用传递”,因此函数调用的目的只是为了避免重复的代码块。但是,似乎没有办法避免写时复制语义。

另外,我不可能(据我所知)创建一个脚本(不是函数)来实现这一点,因为脚本必须包含与调用脚本相同的变量名,所以我需要为每个脚本创建一个不同的脚本我想运行脚本的数组,什么都没有(我仍然会有重复的代码块)。

我已经考虑创建一个别名变量名来“替换”感兴趣的数组变量名,在这种情况下我可以调用脚本并避免重复的代码。但是,我找不到任何在Matlab中创建别名的方法。

最后,我尝试编写一个利用evalin()函数的函数,并将数组变量的 string 名称传递给此函数,但是虽然这样可行,但性能也是如此速度要慢得多 - 与将数组按值传递给函数(性能至少降低10倍)大致相同。

我得出的结论是,在Matlab中不可能避免在非稀疏数组上执行复杂操作时重复代码块,以避免Matlab似乎使用任何可能的避免重复技术而产生的可怕开销。代码块。

我觉得这很难相信,但我无法找到解决办法。

在Matlab中对多个非稀疏数组执行相同的复杂操作时,有没有人知道避免重复代码块的方法?

5 个答案:

答案 0 :(得分:9)

Loren on his blog所述,MATLAB支持矩阵上的内联操作,主要包括通过引用传递数组,在函数中修改它们,以及返回结果。您似乎知道这一点,但是您错误地声明了,因为脚本必须包含与调用脚本相同的变量名称。这是代码示例,显示这是错误的。测试时,请逐字复制并保存为功能:

function inplace_test
y = zeros(1,1e8);
x = zeros(1,1e8);

tic; x = compute(x); toc
tic; y = compute(y); toc
tic; x = computeIP(x); toc
tic; y = computeIP(y); toc
tic; x = x+1; toc
end

function x=computeIP(x)
x = x+1;
end

function y=compute(x)
y = x+1;
end

计算机上的时间结果:

Elapsed time is 0.243335 seconds.
Elapsed time is 0.251495 seconds.
Elapsed time is 0.090949 seconds.
Elapsed time is 0.088894 seconds.
Elapsed time is 0.090638 seconds.

如您所见,对于输入数组xy,使用就地函数的最后两个调用同样快。此外,它们与没有函数的x = x+1运行速度相同。唯一重要的是函数内部输入和输出参数是相同的。还有一件事......

如果我猜测你的代码出了什么问题,我会说你做了嵌套函数,你希望它们就位。他们不是。所以下面的代码不起作用:

function inplace_test
y = zeros(1,1e8);
x = zeros(1,1e8);

tic; x = compute(x); toc
tic; y = compute(y); toc
tic; x = computeIP(x); toc
tic; y = computeIP(y); toc
tic; x = x+1; toc

    function x=computeIP(x)
        x = x+1;
    end

    function y=compute(x)
        y = x+1;
    end
end

Elapsed time is 0.247798 seconds.
Elapsed time is 0.257521 seconds.
Elapsed time is 0.229774 seconds.
Elapsed time is 0.237215 seconds.
Elapsed time is 0.090446 seconds.

底线 - 小心那些嵌套函数..

答案 1 :(得分:4)

您可以尝试将所有数组放入单个单元格数组并在其上使用索引,而不是通过名称引用。 函数仍将复制数组,但脚本可以完成这项工作。

答案 2 :(得分:2)

另一个答案:

有一篇好文章In-place Operations on Data。显然,可能存在两个陷阱:

  1. (这很简单,你可能已经做到了)你不仅要在函数的定义中使用相同的输入和输出变量名,还要在你调用的地方使用相同的变量名。
  2. 仅当您从另一个功能调用您的函数而不是从命令行调用函数时,此方法才有效。很奇怪......我试过了,虽然有一个开销,但它非常小(对于10000×10000阵列,从命令行开始是1秒,从另一个函数开始是0.000361秒)。
  3. 如果这对您不起作用,您可以使用一个未记录的功能,允许您在C ++ MEX文件中进行就地操作。这很讨厌,但这里有一篇文章:Matlab mex in-place editing

答案 3 :(得分:2)

Brian L建议的句柄解决方案确实有效,尽管修改包装数据的第一个调用确实需要很长时间(因为它必须复制原始数据)。

试试这个:

SomeData.m

classdef SomeData < handle
    properties        
            X
    end
    methods                
        function obj = SomeData(x)            
            if nargin > 0
                obj.X = x;
            else
                obj.X = [];
            end
        end
    end
end

LargeOp.m

function directArray = LargeOp( someData, directArray )
    if nargin > 1
        directArray(1,1) = rand(1);
    else
        someData.X(1,1) = rand(1);
        directArray = [];    
    end
end

测试性能的脚本

large = zeros(10000,10000);

data = SomeData(large);

tic
LargeOp(data);
toc

tic
large = LargeOp(data,large);
toc

tic
LargeOp(data);
toc

tic
large = LargeOp(data,large);
toc

结果

Elapsed time is 0.364589 seconds.
Elapsed time is 0.450668 seconds.
Elapsed time is 0.001073 seconds.
Elapsed time is 0.443150 seconds.

答案 4 :(得分:1)

根据您的需要,您可以通过制作nested function来完成此操作。

function A = evensarenegative(n)
    A = zeros(n,1);

    for i = 1:n
        if mod(i,2)
            nested1(i)
        else
            nested2(i)
        end
    end

    function nested1(i)
        A(i) = i;
    end

    function nested2(i)
        A(i) = -i;
    end
end

这里,函数共享相同的工作空间,特别是A矩阵,因此不会复制任何变量。我发现这是一种组织代码的便捷方式,特别是当我在很大的工作流程中有很多次要(但可能是详细的)操作时。