Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/matlab/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MATLAB中高效的阵列预分配_Matlab_Memory Management_Octave - Fatal编程技术网

MATLAB中高效的阵列预分配

MATLAB中高效的阵列预分配,matlab,memory-management,octave,Matlab,Memory Management,Octave,在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 此处的“坏”变量需要O(N^2)时间才能运行,因为它必须在循环的每次迭代中分配新数组并复制旧值 我自己在调试时的首选做法是分配一个带

在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
此处的“坏”变量需要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
然而,有人会天真地认为,一旦我们的代码被调试,我们就在浪费时间,分配一个数组,然后用
0
NaN
填充它。如前所述,您可能可以创建一个未初始化的数组,如下所示

% Method 3 : Even Better?
clear a; a(N,1) = 0;
for i=1:N
    a(i) = cos(i);
end
然而,在我自己的测试(MatlabR2013A)中,我注意到方法1和方法3之间没有明显的差异,而方法2需要更多的时间。这表明当调用
a=zeros(N,1)
时,MATLAB避免了显式地将数组初始化为零

因此,我很想知道

  • 在MATLAB中预分配(未初始化)数组的最佳方法是什么?(最重要的是,大型阵列)
  • 这也适用于八度音阶吗

让Matlab为您处理分配怎么样

clear a;
for i=N:-1:1
    a(i) = cos(i);
end

然后Matlab可以分配并填充它认为最优的数组(可能为零)。但是,您没有
NaNs
的调试优势。

测试

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

方法1(M1R、M1C)

方法2 M2R,M2C

方法3(M3)

结果

时序结果和元素数量已在图timing1D中以双对数标度绘制

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

讨论

MatLab使用JIT(准时制)进行了大量代码优化,即在运行时进行代码优化。因此,提出代码运行更快的部分是由于编程(无论是否优化,始终相同)还是由于优化,这是一个有效的问题。要测试此优化,可以使用功能('accel','off')关闭。再次运行代码的结果非常有趣:

结果表明,对于行向量和列向量,现在方法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复制矩阵的方式),但是
tic;a=NaN(1e4);a(1)=1;toc确实比tic慢;a=零(1e4);a(1)=1;我的机器上的toc
。不过,作为一个提醒,我只看到过使用
零进行预分配,所以我非常确定,除非你做一个mex例程,否则没有一种方法可以不初始化就进行预分配,但这里的其他人可能会知道。这很快成为Matlab常见问题解答,并且这个问题的各个方面已经在这里讨论过了。在其他地方,比如在无价的未记录的Matlab博客上——随着Matlab的发展,各种方法的相对速度似乎也在变化。@Shai这是关于预分配方法的性能,而不是关于预分配的需要。请不要结束这样的问题。@EJG89我觉得这些问题已经足够接近“以dup结束”了。我知道你不同意我的意见,这很好。对于此类分歧,可以选择投票重新开放。非常欢迎你投票支持重新开放。够近了吗?不同的内存预分配方式及其性能是如何满足内存预分配需求的。讨论的水平更高了,我不明白你为什么要这么匆忙地结束问题。我可能没有代表投票支持重新开放,所以你似乎只是在自己玩。你只是不想让人们讨论MatLab的深层次/内部工作。另一个话题甚至没有涉及JIT,我想这是这个问题的基础。你有没有计时?我假设它与方法(3)相同,我没有计时,但我也希望如此(对于大多数情况)。
a = NaN(1,n);
a(n) = 0;
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