首页 文章

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

提问于
浏览
4

我正在尝试学习MATLAB,我遇到的第一个问题是用静态相机和移动物体来猜测图像序列的背景 . 首先,我只想对像素进行均值或中位数,因此它是 just a single function I would like to apply to one of the rows of the 4 dimensional array .

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

uint8 [ num_images, width, height, RGB ]

这是我写的函数,包括4个嵌套循环 . 我使用 preallocation 但仍然非常慢 . 在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 seconds vs. 20 seconds 中完成,因此只需编写一个较小的函数就可以达到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

所以现在我不明白 if MATLAB could be this fast then why is the nested loop version so slow? 我没有进行任何动态调整大小,MATLAB必须在里面运行相同的4个嵌套循环 .

Why is this happening?

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

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

Update

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

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

How to write vectorized functions in MATLAB

3 回答

  • 2

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

  • 4

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

    评估是一个漫长的过程,因为它必须解析,'compile'并解释每一行 * . 使用具有简单操作的for循环意味着matlab比实际执行代码花费更多时间进行解析/编译 .

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

    底线:我们习惯于过程语言和循环,但几乎总是以矢量化的方式做同样的事情 .

    * 要完成并向荣誉到期的人致敬:最近版本的Matlab实际上试图通过分析重复操作来加速循环,以将重复操作的块编译成本机可执行文件 . 这称为Just In Time编译(JIT),并在以下注释中由Jonas指出 .


    原始答案:

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

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

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

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

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

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

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

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

相关问题