gpt4 book ai didi

matlab - MATLAB 中的高效数组预分配

转载 作者:太空宇宙 更新时间:2023-11-03 19:15:10 25 4
gpt4 key购买 nike

在 MATLAB 中高效编程的首要任务之一是避免动态调整数组大小。标准示例如下。

N = 1000;

% Method 0: Bad
clear a
for i=1:N
a(i) = cos(i);
end

% Method 1: Better
clear a; a = zeros(N,1);
for i=1:N
a(i) = cos(i)
end

此处的“Bad”变体需要 O(N^2) 时间才能运行,因为它必须在循环的每次迭代中分配一个新数组并复制旧值。

我自己在调试时的首选做法是分配一个带有 NaN 的数组,比 0 更不容易与有效值混淆。

% Method 2: Easier to Debug
clear a; a = NaN(N,1);
for i=1:N
a(i) = cos(i)
end

但是,有人会天真地认为一旦我们的代码被调试,我们通过分配一个数组然后用 0NaN 填充它是在浪费时间。如前所述here ,您或许可以创建一个未初始化的数组,如下所示

% Method 3 : Even Better?
clear a; a(N,1) = 0;
for i=1:N
a(i) = cos(i);
end

但是,在我自己的测试 (MATLAB R2013a) 中,我注意到方法 1 和方法 3 之间没有明显差异,而方法 2 需要更多时间。这表明 MATLAB 在调用 a = zeros(N,1) 时避免了将数组显式初始化为零。

所以我很想知道

  • 在 MATLAB 中预分配(未初始化的)数组的最佳方法是什么? (最重要的是,大型阵列)
  • 这在 Octave 中也成立吗?

最佳答案

测试

我使用 MatLab 2013b I 和 Intel Xeon 3.6GHz + 16GB RAM 运行下面的代码来进行分析。我区分了 3 种方法,只考虑了一维数组,即向量。方法 1 和方法 2 已使用列向量和行向量进行测试,即 (n,1) 和 (1,n)。

方法一(M1R、M1C)

a = zeros(1,n);

方法二M2R、M2C

a = NaN(1,n);

方法 3 (M3)

a(n) = 0;

结果

计时结果和元素数量已绘制在图 timing1D 中的双对数刻度上。

timings1d

如图所示,第三种方法的赋值几乎与向量大小无关,而另一种方法则稳步增加,表明向量的隐式定义。

讨论

MatLab 使用 JIT(Just in time)做了很多代码优化,即在运行时优化代码。因此,提出运行得更快的代码部分是由于编程(无论是否优化总是相同的)还是由于优化是一个有效的问题。要测试此优化,可以使用 feature('accel','off') 关闭。再次运行代码的结果比较有意思:

timings1Dnoacceleration

结果表明,现在方法 1 对于行向量和列向量都是最优的。方法 3 的行为与第一个测试中的其他方法一样。

结论

优化内存预分配是无用的,而且是浪费时间,因为 MatLab 无论如何都会为您优化。

请注意,内存应该是预先分配的,但分配方式并不重要。预分配内存的性能在很大程度上取决于 MatLab 的 JIT 编译器是否选择优化您的代码。这完全取决于您的 .m 文件的所有其他内容,因为编译器当时会考虑代码块,然后尝试优化(它甚至有内存,因此多次运行文件可能会导致执行速度更低-时间)。考虑到与之后执行的计算相比的性能,内存预分配通常是一个非常短的过程

在我看来,应该使用方法 1 或方法 2 来预分配内存,以保持代码可读,并使用 MatLab 帮助建议的功能,因为这些功能将来最有可能得到改进。

使用的代码

clear all
clc
feature('accel','on')

number1D=30;

nn1D=2.^(1:number1D);

timings1D=zeros(5,number1D);

for ii=1:length(nn1D);
n=nn1D(ii);
% 1D
tic
a = zeros(1,n);
a(randi(n,1))=1;
timings1D(1,ii)=toc;
fprintf('1D row vector method1 took: %f\n',timings1D(1,ii))
clear a

tic
b = zeros(n,1);
b(randi(n,1))=1;
timings1D(2,ii)=toc;
fprintf('1D column vector method1 took: %f\n',timings1D(2,ii))
clear b

tic
c = NaN(1,n);
c(randi(n,1))=1;
timings1D(3,ii)=toc;
fprintf('1D row vector method2 took: %f\n',timings1D(3,ii))
clear c

tic
d = NaN(n,1);
d(randi(n,1))=1;
timings1D(4,ii)=toc;
fprintf('1D row vector method2 took: %f\n',timings1D(4,ii))
clear d

tic
e(n) = 0;
e(randi(n,1))=1;
timings1D(5,ii)=toc;
fprintf('1D row vector method3 took: %f\n',timings1D(5,ii))
clear e
end
logtimings1D = log10(timings1D);
lognn1D=log10(nn1D);
figure(1)
clf()
hold on
plot(lognn1D,logtimings1D(1,:),'-k','LineWidth',2)
plot(lognn1D,logtimings1D(2,:),'--k','LineWidth',2)
plot(lognn1D,logtimings1D(3,:),'-.k','LineWidth',2)
plot(lognn1D,logtimings1D(4,:),'-','Color',[0.6 0.6 0.6],'LineWidth',2)
plot(lognn1D,logtimings1D(5,:),'--','Color',[0.6 0.6 0.6],'LineWidth',2)
xlabel('Number of elements (log10[-])')
ylabel('Timing of each method (log10[s])')
legend('M1R','M1C','M2R','M2C','M3','Location','NW')
title({'Various methods of pre-allocation in 1D','nr. of elements vs timing'})
hold off

注意

包含c(randi(n,1))=1的行;除了将值 1 分配给预分配数组中的随机元素之外,不要做任何事情,以便使用该数组对 JIT 编译器进行一些挑战。这些行不会显着影响预分配测量,即它们不可测量且不影响测试。

关于matlab - MATLAB 中的高效数组预分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18276249/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com