在大模拟中改进Matlab功能

时间:2014-12-05 18:02:47

标签: performance matlab for-loop vectorization

我手中有一个非常大的Matlab仿真项目,我想优化它,因为我经常运行它来调整参数等。

使用Matlab的profile我确定了一个耗费大部分时间的功能,特别是行output(i,1)= max(mean(dens(i+1:a,1)),dens(i+1,1));

此功能称为 LOT ,其中input10x1双作为参数传递,output也是10x1矢量。

function output = my_function(input)

a = size(input,1);
output = input*0;
dens = density(input);

% for each i, output(i) is the maximum between output(i+1) and mean(output(i+1:end))
for i = 1:a-1
    output(i,1)= max(mean(dens(i+1:a,1)),dens(i+1,1));
end
output(a,1) = dens(a,1);

end

我的想法:

  • 我认为矢量化可能有助于摆脱循环(?),但我对这项技术并不熟悉。
  • 是否有更快/替代的方式来计算mean(可能没有Matlab的内置函数调用?)

修改 我试图对该函数进行向量化,并得到以下替代结果,它执行相同的操作:

function output = my_function_vectorized(input)

a = size(input,1);
rho_ref = zeros(size(input));
dens = density(input);

temp_cumsum = flip(cumsum(flip(dens))./(1:1:a)');
output = [max(temp_cumsum(2:end),dens(2:a));dens(a)];

end

我尝试以下列方式执行测试这两项功能:

Ts = random('unif',40,80,10,1000);
Results_original = zeros(size(Ts));
Results_vectorized = zeros(size(Ts));
TIMES_original = zeros(size(Ts,2),1);
TIMES_vectorized = zeros(size(Ts,2),1);

for ii = 1:size(Ts,2)
    tic;
    Results_original(:,ii) = my_function(Ts(:,ii));
    TIMES_original(ii) = toc;
end

for ii = 1:size(Ts,2)
    tic;
    Results_vectorized(:,ii) = my_function_vectorized(Ts(:,ii));
    TIMES_vectorized(ii) = toc;
end

res = norm(Res_1 - Res_2);
mTIMES_original = mean(TIMES_original);
mTIMES_vectorized = mean(TIMES_vectorized);

我得到了:

res =

   3.1815e-12

mTIMES_original/mTIMEZ_vectorized =

   3.0279
  • 这个遗留物应该对我有用吗?
  • 说我把这个计算固定了3倍是否正确?

2 个答案:

答案 0 :(得分:2)

矢量化它。

重新阅读窝点是杀死你的东西,而不是平均值。平均值与Donald Knuth可以做到的一样优化。

我不知道你的密度函数,所以我无法确定我的索引。

Pseudocode剪辑:

%(1)faster predeclaration that shows intent
output=zeroes(size(input))

%(2)vectorize your "mean between here and the end"
b = fliplr(fliplr(cumsum(dens(1:a-1)))./fliplr(1:a-1))

%(3)assemble your interior nX2 matrix 
c = [b,dens]

%(4)vectorized max, I think
output = max(c,[],2)

(1)速度和效率很难击败内置插件。能够从现在开始计算出你的代码所做的事情也很好。随着时间的推移,我发现自己越来越多地成为一名有文化的程序员(link),因为从长远来看,它比在一年或十年内回来并试图逆向工程自己的工作要花费更少的时间。

(2)这里的想法是将密度向量翻转,然后计算累积和,然后将反向累积和的每个元素除以进入它的点数,然后再将其翻转。当你将这笔金额除以计数时 - 它就变成了一个平均值。我只是阅读了描述(链接)并且有一个内部开关,所以你可以在不使用fliplr的情况下重新设置它,并使其更快。

b = cumsum(dens(1:a-1),'reverse')./(a-1:-1:1) %this might work

(3)理论上,当这样做时,你应该有一个两列宽的矩阵,并且行数和#34; dens"确实。调整大小和预定义可能很昂贵 - 因此,如果您经常更改大小,那么您可能希望将其预先声明为(1)。

(4)" max"功能也会快速尖叫。不是你和Knuth先生都不会让它更快。我认为对于数组的每个元素进行一次比较(硅操作)和一些shuffle(每个元素少于一个)都是必需的。

这是一个以元素为单位的最大值。 (我忘了在中间添加缓冲区)。它已经很快,它的输出是一个数组。它可能需要1而不是2,但你知道你在那里做了什么,并且可以解决这个问题。

如果这对你有用,请告诉我。我估计它可能不会提高5倍。

我惊讶地发现LabVIEW可以比MatLab快100倍的基础,因为它(总是)被编译。在MatLab中进行编译时,必须对类型和值施加许多新约束,但在LV中,编译主要是无痛的,因为所有约束都是初始程序创建的一部分。如果您发现MatLab程序的核心速度不够快,您可以为LV创建一个包装器,并且可以更快地运行它(更多),并且很少有心痛。 LV没有详细说明 - 有一个原因我们使用文本来代替图片(或达芬奇个性化的主题效果图,作为一个更正确的比喻)。

编辑:(关于速度)

看起来你快〜3倍。

编辑:(关于代码,请注意我使用2014a)

clc; format short g;
a = 1:15
mu = fliplr(cumsum(fliplr(a))./(1:length(a)))

给出:

a =

     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15


mu =

  Columns 1 through 9

            8          8.5            9          9.5           10         10.5           11         11.5           12

  Columns 10 through 15

         12.5           13         13.5           14         14.5           15

所以我制作" a",一个从1开始到15的矢量。最后一个值是15.第二个到最后一个值之间的平均值,最后一个是14.5。最后3个值的平均值是14.数学似乎在这里工作。

修改

一个很大的加速是关闭当前基于java的系统。我已经看到代码通过运行版本2010a获得了大(优于3倍)的速度提升。通过Java运行时,某些代码的运行速度远远低于通过Fortran或基于C的编译库运行时的运行速度。

答案 1 :(得分:2)

正如已经建议的那样,您可以考虑对代码进行矢量化;但实际上,在这种情况下,我不确定能提供多少改进。首先,请记住,虽然旧版本的MATLAB for 循环通常被认为与矢量化方法相比效率非常低,但由于现代MATLAB中的JIT加速器, for 循环不像几年前那样是一个大问题(性能明智)。

其次,考虑一下如果你必须跳过箍试图将你的数据变成一个可以执行矢量化命令的形式(看起来可能就是这种情况,这里),那么它可能是一个洗 - 含义执行矢量化命令的性能优势超过了将数据操作为必要的矢量化形式所需的时间(并且可能使您的代码完全不可读,容易出现潜在错误,并且难以维护)。

当然,这并不是说矢量化在你的情况下根本不会有所帮助(唯一真正的知道方法是给它一个镜头并对其进行分析),但只是意识到潜在的局限性

除了EngrStudent提出的建议外,我还建议你看一下这篇文章 来自MathWorks的 Accelerating MATLAB Algorithms and Applications

特别是,本文中描述的两个选项在我看来对你的案例有所帮助。

首先是将您的函数转换为 MATLAB可执行文件(MEX函数)。这是一个相当简单的过程,涉及使用 MATLAB Coder 从您的函数自动生成C代码,然后可以将其编译为可执行的MEX函数。我怀疑这为性能提升提供了最大的潜力。 (如果你没有 MATLAB Coder 工具箱,你也可以考虑手动编写你的函数的C代码版本(或者至少是它的时间密集部分)并使用它来你可以在MATLAB中使用的produce a MEX function

第二种方法是使用并行计算。例如,因为 for 循环的每次迭代都是彼此独立的,所以你可以用 parallel for 循环替换它( parfor ) 。此外,您的总体系统或工作流程的其他部分也许可以并行化。这种方法显然需要访问并行计算工具箱以及多核处理器(或集群),因此这可能对您有限...但如果您有权访问那些资源,那么这可能对绩效非常有益。