【问题标题】:Insights about how MATLAB interpreter works关于 MATLAB 解释器工作原理的见解
【发布时间】:2021-02-14 03:19:06
【问题描述】:

我正在尝试加速 MATLAB 代码,该代码需要在 for 循环中访问大型矩阵 a 的一些术语 a(i,j)。在某些部分中,在五次或更多不同的计算中可能需要一项。在这些情况下,代码会将术语 a(i,j) 分配给另一个变量 k

我认为这会产生不必要的分配(在五次计算的情况下),但令我惊讶的是,事实恰恰相反。实际上,这个赋值确实使代码运行得更快。访问五次大型矩阵的项比将其传递给标量变量并访问此标量变量五次要慢。

这些发现可以在一个简单的测试函数中重现:

r = 5e6;

i = 50;
j = 50000;

a = zeros(i,j);

% 

tic
for ii = 1:r
    b = a(i,j)+a(i,j)+a(i,j)+a(i,j)+a(i,j);
end
toc

%

tic
for ii = 1:r
    k = a(i,j);
    b = k+k+k+k+k;
end
toc

第一个代码比第二个代码花费的时间多 3.5 倍

MATLAB 从 ~20 Mb 的矩阵中访问数据应该那么慢吗?

编辑 1:

按照 Cris Luengo 的回答,显然我正在使用的 MATLAB 安装存在问题 (R2019a)。之前的结果是通过一个M文件得到的。

以下代码生成output。好像根本没有编译。

r = 5e6;

i = 50;
j = 50000;

a = zeros(i,j);

aux_rgb = lines(2);

figure('Color','White','Name','Code with drawnow'); hold on;
legend('location','bestoutside'); ylim([0,1.05]);
xlabel('number of terms in summation');
ylabel('relative time spent');

h1 = animatedline(NaN,NaN,'LineWidth',2.5,'Color',aux_rgb(1,:),'DisplayName','a(i,j)');
h2 = animatedline(NaN,NaN,'LineWidth',2.5,'Color',aux_rgb(2,:),'DisplayName','k');

%%

n = 1;

t1 = tic;
for ii = 1:r
    b = a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);

t2 = tic;
for ii = 1:r
    k = a(i,j);
    b = k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);

drawnow

%%

n = 2;

t1 = tic;
for ii = 1:r
    b = a(i,j)+a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);

t2 = tic;
for ii = 1:r
    k = a(i,j);
    b = k+k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);

drawnow

%%

n = 3;

t1 = tic;
for ii = 1:r
    b = a(i,j)+a(i,j)+a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);

t2 = tic;
for ii = 1:r
    k = a(i,j);
    b = k+k+k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);

drawnow

%%

n = 4;

t1 = tic;
for ii = 1:r
    b = a(i,j)+a(i,j)+a(i,j)+a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);

t2 = tic;
for ii = 1:r
    k = a(i,j);
    b = k+k+k+k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);

drawnow

%%

n = 5;

t1 = tic;

for ii = 1:r
    b = a(i,j)+a(i,j)+a(i,j)+a(i,j)+a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);

t2 = tic;
for ii = 1:r
    k = a(i,j);
    b = k+k+k+k+k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);

drawnow

编辑 2:

再次按照Cris Luengo的回答,就是通过M文件函数(不是M文件脚本)获得的output。现在编译完成了它的工作。

【问题讨论】:

    标签: matlab matrix indexing interpreter


    【解决方案1】:

    通过复制粘贴到命令行或函数内部来运行代码会有所不同。我明白了:

    Elapsed time is 0.062195 seconds.
    Elapsed time is 0.034381 seconds.
    

    在命令行中复制粘贴时,以及

    Elapsed time is 0.024922 seconds.
    Elapsed time is 0.025392 seconds.
    

    如果我创建一个包含相同代码的函数 M 文件并运行该函数(函数 M 文件有一行以 function 关键字开头的行作为第一个非注释行)。在这种情况下,两个循环的速度一样快(第一个可能会稍快一些,但差别很小)。

    您还会注意到,这两个循环的运行速度都比在命令行中复制粘贴时要快。当运行一个函数时,该函数被编译然后执行(这被称为“即时编译”,或 JIT)。该编译器能够优化多个索引操作,有效地索引一次。

    在命令行上,不进行编译,因此索引位置被计算了五次,值被检索了五次。

    脚本 M 文件(不以 function 关键字开头的 M 文件)应该进行 JIT 编译,但这似乎并不总是发生,至少在进行相同数量的优化时不会发生这种情况。

    【讨论】:

    • 我同意克里斯在这里的回答。我还要提一下,我认为 why 起作用的原因是,当您在 Matlab 中执行 a(i,j) 时,您不仅仅是引用数组中的原始元素(如 C 或 Java 那样),但您实际上是在构建一个包含该值的新 1×1 mxArray,这会产生开销。所以预计在正常情况下,做k = a(i,j);和重用k应该比重复a(i,j)引用要快。只有 JIT 可以优化这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 2015-12-29
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 1970-01-01
    相关资源
    最近更新 更多