我在Matlab脚本中有复杂的代码块,它们作用于大型非稀疏数组。该代码对数组中的随机元素以及读取操作执行许多写操作。相同的代码必须针对不同的(大型)数组执行(即,相同的代码块,不同的数组变量名称除外)。
我不希望只有数组名称不同的长重复代码块。
不幸的是,当我创建一个函数来执行操作时,代码块只出现一次,性能会降低10倍或更多(可能是由于复制了数组)。但是,我不需要复制数组。我宁愿“通过引用传递”,因此函数调用的目的只是为了避免重复的代码块。但是,似乎没有办法避免写时复制语义。
另外,我不可能(据我所知)创建一个脚本(不是函数)来实现这一点,因为脚本必须包含与调用脚本相同的变量名,所以我需要为每个脚本创建一个不同的脚本我想运行脚本的数组,什么都没有(我仍然会有重复的代码块)。
我已经考虑创建一个别名变量名来“替换”感兴趣的数组变量名,在这种情况下我可以调用脚本并避免重复的代码。但是,我找不到任何在Matlab中创建别名的方法。
最后,我尝试编写一个利用evalin()
函数的函数,并将数组变量的 string 名称传递给此函数,但是虽然这样可行,但性能也是如此速度要慢得多 - 与将数组按值传递给函数(性能至少降低10倍)大致相同。
我得出的结论是,在Matlab中不可能避免在非稀疏数组上执行复杂操作时重复代码块,以避免Matlab似乎使用任何可能的避免重复技术而产生的可怕开销。代码块。
我觉得这很难相信,但我无法找到解决办法。
在Matlab中对多个非稀疏数组执行相同的复杂操作时,有没有人知道避免重复代码块的方法?
答案 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.
如您所见,对于输入数组x
和y
,使用就地函数的最后两个调用同样快。此外,它们与没有函数的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。显然,可能存在两个陷阱:
如果这对您不起作用,您可以使用一个未记录的功能,允许您在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
矩阵,因此不会复制任何变量。我发现这是一种组织代码的便捷方式,特别是当我在很大的工作流程中有很多次要(但可能是详细的)操作时。