【问题标题】:Improving Matlab function within big simulation在大仿真中改进 Matlab 函数
【发布时间】:2025-12-24 08:20:16
【问题描述】:

我手头有一个非常大的 Matlab 仿真项目,我想对其进行优化,因为我多次运行它来调整参数等。

使用 Matlab 的 profile 我发现了一个占用我大部分时间的函数,特别是 output(i,1)= max(mean(dens(i+1:a,1)),dens(i+1,1)); 这一行

此函数称为 LOT,其中 input 是作为参数传递的 10x1 double,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 倍是否正确?

【问题讨论】:

    标签: performance matlab for-loop vectorization


    【解决方案1】:

    矢量化它。

    重读 dens 是要杀死你的东西,而不是平均值。平均值是 Donald Knuth 可以做到的优化。

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

    伪代码片段:

    %(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) 这里的想法是翻转密度向量,然后进行累积和,然后将反向累积和的每个元素除以输入的点数,然后再次翻转。当您将该总和除以计数时 - 它变成了一个平均值。我刚刚阅读了描述(链接),并且有一个内部开关,因此您可以在没有翻转器的情况下重述它并使其更快。

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

    (3) 理论上,完成此操作后,您应该有一个两列宽的矩阵,并且具有与“dens”一样多的行。调整大小和预先声明可能会很昂贵 - 因此,如果您经常更改大小,那么您可能希望像 (1) 一样预先声明它。

    (4) "max" 函数也会快速尖叫。你和 Knuth 先生都不会让它更快。我认为只需要对数组的每个元素进行一次比较(硅运算)和一些随机播放(每个元素少于一次)。

    这是一个元素方面的最大值。 (我忘了在中间添加缓冲区)。它已经做得很快了,它的输出是一个数组。它可能需要 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 的编译库运行时要慢得多。

    【讨论】:

    • 谢谢,你能解释一下你对最后一个max 做了什么吗?我正在检查这是否适用于我的实现。另外,最后一段很好:)
    • 好的,所以你的矢量化方法,但是函数cumsum有一个大问题。它将所有值显示为具有指数的向量:1.0e+03 * 整个向量。总而言之,我将这些值与为每个 k 手动执行 sum(dens(k:a)) 进行了比较,它们有所不同。关于如何使cumsum 在这种情况下正常工作的想法?
    • 我在家里使用“R”。答案将等到星期一我再次可以访问付费许可的 MatLab 时。 (可能在午餐时间)
    • LabVIEW MathScript 是经过编译的旧版 MatLab。如果您的 MatLab(或 MATLAB 代码)工作正常,不依赖于其他工具箱,并且您在 LabVIEW 中关闭调试,那么您可以在相同代码上获得约 1000 倍的加速。
    【解决方案2】:

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

    其次,请考虑如果您必须跳过箍来尝试将数据转换为可以执行矢量化命令的形式(看起来可能是这种情况,这里),那么它可能是一个洗涤 - 意思执行矢量化命令所带来的性能优势超过了将数据操作为必要的矢量化形式所花费的时间(并且可能使您的代码完全不可读、存在潜在错误并且难以维护)。

    当然,这并不是说矢量化对您的情况完全没有帮助(唯一真正了解的方法是试一试并分析它),而只是意识到潜在的局限性。

    除了EngrStudent的建议,我也建议看看这篇文章 Accelerating MATLAB Algorithms and Applications 来自 MathWorks。

    特别是,本文中描述的两个选项让我觉得可能对您的情况有所帮助。

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

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

    【讨论】: