8

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

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

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

此外,不可能(据我了解)创建一个脚本(不是函数)来实现这一点,因为脚本必须包含与调用脚本相同的变量名,所以我需要为每个数组使用不同的脚本我希望运行脚本,但没有任何收获(我仍然会有重复的代码块)。

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

最后,我尝试编写一个利用该函数的函数,并将数组变量的字符串evalin()名称传递给该函数,但尽管这可行,但性能也慢得多——与将数组按值传递给函数大致相同(性能至少下降 10 倍)。

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

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

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

4

5 回答 5

9

正如Loren 在他的博客中指出的那样,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.

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

于 2012-10-26T06:14:02.777 回答
4

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

于 2012-10-25T23:42:30.963 回答
2

另一个答案:

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

  1. (这是微不足道的,你可能做到了)你应该使用相同的输入和输出变量名,不仅在函数的定义中,而且在你调用它的地方。
  2. 这仅在您从 ANOTHER FUNCTION 而不是从命令行调用函数时才有效。奇怪......我试过了,虽然有开销,但它非常小(对于 10000 x 10000 阵列,从命令行需要 1 秒,从另一个函数需要 0.000361 秒)。

如果这对您不起作用,您可以使用未记录的功能,该功能允许您在 C++ MEX 文件中进行就地操作。这很讨厌,但这里有一篇关于此的文章:Matlab mex in-place editing

于 2012-10-26T01:08:02.297 回答
2

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

试试这个:

一些数据.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

大运算

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.
于 2012-10-26T01:27:27.643 回答
1

根据您的需要,您可以通过创建嵌套函数来完成此操作。

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矩阵,因此不会复制任何变量。我发现它是一种组织代码的便捷方式,尤其是当我有很多次要(但可能是冗长的)操作作为更大工作流程的一部分时。

于 2012-10-25T23:14:54.417 回答