嵌套for循环在MATLAB中非常慢(预分配)

时间:2011-10-18 17:25:26

标签: arrays performance matlab loops for-loop

我正在尝试学习MATLAB,我遇到的第一个问题是用静态相机和移动物体来猜测图像序列的背景。首先,我只是想在一段时间内对像素进行均值或中值,所以它只是我想要应用于4维数组的一行的单个函数。

我已将RGB图像加载到具有以下尺寸的4维阵列中:

uint8 [ num_images, width, height, RGB ]

这是我写的函数,包括4个嵌套循环。我使用预分配,但仍然非常慢。在C ++中,我相信这个函数的运行速度至少要快10倍到20倍,我认为在CUDA上它实际上可以实时运行。在MATLAB中,4个嵌套循环大约需要20秒。我的堆栈是100个图像,尺寸为640x480x3。

function background = calc_background(stack)
tic;

si = size(stack,1);
sy = size(stack,2);
sx = size(stack,3);
sc = size(stack,4);

background = zeros(sy,sx,sc);
A = zeros(si,1);

for x = 1:sx
    for y = 1:sy
        for c = 1:sc
            for i = 1:si
                A(i) = stack(i,y,x,c);
            end
            background(y,x,c) = median(A);
        end
    end
end

background = uint8(background);

disp(toc);
end

你能告诉我如何更快地制作这段代码吗?我尝试过以某种方式直接从数组中仅使用索引获取数据,并且它似乎更快。它在 3秒与20秒完成,因此只需编写一个较小的函数就可以达到7倍的性能差异。

function background = calc_background2(stack)
    tic;

    % bad code, confusing
    % background = uint8(squeeze(median(stack(:, 1:size(stack,2), 1:size(stack,3), 1:3 ))));

    % good code (credits: Laurent)
    background=uint8((squeeze(median(stack,1)));

    disp(toc);
end

所以现在我不明白如果MATLAB可以这么快那么为什么嵌套循环版本这么慢?我没有进行任何动态调整大小并且MATLAB必须运行相同的4个嵌套循环内部。

为什么会这样?

有没有办法让嵌套循环快速运行,就像在C ++中自然会发生一样?

或者我是否应该习惯于用这种疯狂的单行语句编程MATLAB以获得最佳性能?

更新

感谢所有的好答案,现在我了解了很多。我使用stack(:, 1:size(stack,2), 1:size(stack,3), 1:3 ))的原始代码没有任何意义,它与stack完全相同,我很幸运,中位数的默认选项是将第一维用于其工作范围。

我认为最好问一下如何在另一个问题中写一个有效的问题,所以我在这里问:

How to write vectorized functions in MATLAB

3 个答案:

答案 0 :(得分:4)

如果我理解你的问题,你就会问为什么Matlab对于矩阵运算比对程序编程调用更快。答案很简单就是that's how it's designed。如果你真的想知道是什么原因,你可以阅读this newsletter from Matlab's website讨论一些基础技术,但你可能不会得到一个很好的答案,因为该软件是专有的。我还通过谷歌搜索找到了一些relevant pagesthis old SO question  似乎也解决了你的问题。

答案 1 :(得分:2)

Matlab是一种解释型语言,意味着它必须评估脚本的每一行代码。

评估是一个漫长的过程,因为它必须解析,“编译”并解释每一行*。 使用简单操作的for循环意味着matlab在解析/编译时花费的时间比实际执行代码要多得多。

另一方面,内置函数以编译语言编码并经过大量优化。他们非常快,因此速度差异。

底线:我们非常习惯于过程语言和循环,但几乎总是有一种很好的快速方式以矢量化的方式做同样的事情。

*要完成并为荣誉到期付出荣誉:Matlab的最新版本实际上试图通过分析重复操作来将循环重复操作编译成本机可执行文件来加速循环。这称为Just In Time编译(JIT),Jonas在以下评论中指出了这一点。


原始答案:

如果我理解得很好(你想要第一个维度的中位数),你可以尝试:

background=uint8((squeeze(median(stack,1)));

答案 2 :(得分:1)

嗯,两者之间的区别在于他们执行代码的方法。要粗略地绘制它:在C中,您将代码提供给编译器,编译器将尝试优化您的代码或无论如何将其转换为机器代码。这需要一些时间,但是当您实际执行程序时,它已经在机器代码中,因此执行速度非常快。您的编译器可能会花费大量时间为您优化代码,通常您不关心编译分发就绪程序是否需要1分钟或10分钟。

MATLAB(和其他解释语言)通常不会那样工作。当您执行程序时,解释器将解释每行代码并将其转换为一系列机器代码。如果你编写for循环,这有点慢,因为它必须一遍又一遍地解释代码(至少在原则上,还有其他开销可能对最新版本的MATLAB更重要)。这里的障碍是必须在运行时完成所有事情:解释器可以执行一些优化,但执行耗时的优化可能会在某些情况下大幅提高性能,因为它们会导致性能受损在大多数其他情况下。

你可能会问你使用MATLAB获得了什么?您可以获得灵活性和清晰的语义。当你想进行矩阵乘法时,你只需要这样写;在C中,这将产生一个双for循环。你不得不担心数据类型,内存管理......

在幕后,MATLAB使用编译代码(Fortan / C / C ++,如果我没有记错的话)来执行大型操作:因此矩阵乘法实际上是由一个机器代码执行的,它是用另一种语言编译的。对于较小的操作,情况也是如此,但您不会注意到这些计算的速度,因为大部分时间都花在管理代码上(传递变量,分配内存,......)。

总结一下:是的,你应该习惯这种紧凑的陈述。如果您看到像Laurent的示例一行代码,您会立即看到它计算堆栈的中位数。您的代码需要11行代码来表达相同的内容,因此当您查看像您这样的代码(可能嵌入数百行其他代码中)时,您将很难理解正在发生的事情并精确定位某些代码。操作完成。

进一步争论:你不应该像在C / C ++中编程一样在MATLAB中编程;你也不应该反过来。每种语言都有更强大和更弱的观点,学会了解它们并使用每种语言来实现它的目的。例如。你可以在MATLAB中编写一个完整的编译器或web服务器,但总的来说,由于MATLAB不打算处理或连接字符串(它可以,但它可能非常慢),因此它会非常慢。