Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-apps-script/6.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
Performance 通过空矩阵乘法初始化数组的更快方法?(Matlab)_Performance_Matlab_Initialization_Matrix Multiplication - Fatal编程技术网

Performance 通过空矩阵乘法初始化数组的更快方法?(Matlab)

Performance 通过空矩阵乘法初始化数组的更快方法?(Matlab),performance,matlab,initialization,matrix-multiplication,Performance,Matlab,Initialization,Matrix Multiplication,在我看来,我偶然发现了Matlab处理问题的奇怪方式。例如,如果两个空矩阵相乘,结果为: zeros(3,0)*zeros(0,3) ans = 0 0 0 0 0 0 0 0 0 现在,这已经让我惊讶了,然而,快速搜索让我找到了上面的链接,我得到了关于为什么会发生这种情况的有点扭曲的逻辑的解释 然而,我对下面的观察毫无准备。我问自己,与仅仅使用zero(n)函数相比,这种乘法的效率有多高,比如说为了初始化?我曾经回答过这个问题: f=@

在我看来,我偶然发现了Matlab处理问题的奇怪方式。例如,如果两个空矩阵相乘,结果为:

zeros(3,0)*zeros(0,3)
ans =

 0     0     0
 0     0     0
 0     0     0
现在,这已经让我惊讶了,然而,快速搜索让我找到了上面的链接,我得到了关于为什么会发生这种情况的有点扭曲的逻辑的解释

然而,我对下面的观察毫无准备。我问自己,与仅仅使用
zero(n)
函数相比,这种乘法的效率有多高,比如说为了初始化?我曾经回答过这个问题:

f=@() zeros(1000)
timeit(f)
ans =
    0.0033
vs:

两者都具有相同的1000x1000类零矩阵的结果
double
,但空矩阵乘法1快约350倍!(使用
tic
toc
以及一个循环,也会产生类似的结果)

这怎么可能?是
timeit
还是
tic,toc
虚张声势,还是我找到了一种更快的方法来初始化矩阵? (这是在win7-64机器、intel-i5 650 3.2Ghz…)上使用matlab 2012a完成的)

编辑:

在阅读了您的反馈之后,我更仔细地研究了这一特性,并在两台不同的计算机上测试了一段代码(尽管是2012a版的matlab),该代码检查了运行时间与矩阵n大小的关系。这就是我得到的:

生成此代码的代码与以前一样使用了
timeit
,但是带有
tic
toc
的循环看起来是一样的。因此,对于小尺寸,
zero(n)
是可比较的。然而,在
n=400左右,空矩阵乘法的性能有了飞跃。我用来生成该绘图的代码是:

n=unique(round(logspace(0,4,200)));
for k=1:length(n)
    f=@() zeros(n(k));
    t1(k)=timeit(f);

    g=@() zeros(n(k),0)*zeros(0,n(k));
    t2(k)=timeit(g);
end

loglog(n,t1,'b',n,t2,'r');
legend('zeros(n)','zeros(n,0)*zeros(0,n)',2);
xlabel('matrix size (n)'); ylabel('time [sec]');
你们中有人也有这种经历吗

编辑#2:

顺便提一下,不需要空矩阵乘法来获得这种效果。我们可以简单地做到:

z(n,n)=0;
其中n>前一个图中所示的一些阈值矩阵大小,并获得与空矩阵乘法(再次使用timeit)相同的精确的效率曲线

下面是一个提高代码效率的示例:

n = 1e4;
clear z1
tic
z1 = zeros( n ); 
for cc = 1 : n
    z1(:,cc)=cc;
end
toc % Elapsed time is 0.445780 seconds.

%%
clear z0
tic
z0 = zeros(n,0)*zeros(0,n);
for cc = 1 : n
    z0(:,cc)=cc;
end
toc % Elapsed time is 0.297953 seconds.

然而,使用
z(n,n)=0产生的结果与
零(n)
情况类似。

这很奇怪,我看到f比你看到的快,而g比你看到的慢。但对我来说,它们都是一样的。也许是不同版本的MATLAB

>> g = @() zeros(1000, 0) * zeros(0, 1000);
>> f = @() zeros(1000)
f =     
    @()zeros(1000)
>> timeit(f)  
ans =    
   8.5019e-04
>> timeit(f)  
ans =    
   8.4627e-04
>> timeit(g)  
ans =    
   8.4627e-04
编辑你能在f和g的末尾加上+1吗,看看你得到的时间是多少

编辑2013年1月6日美国东部时间7:42

我正在远程使用一台机器,对于低质量的图形(不得不盲目生成)感到抱歉

机器配置:

I7920。2.653千兆赫。Linux。12GB内存。8MB缓存

看起来,即使是我接触过的机器也显示出这种行为,除了更大的尺寸(1979年到2073年之间)。我现在想不出有什么理由可以让空矩阵乘法在更大的尺寸下更快

回来之前我会再调查一下

编辑2013年1月11日

在@EitanT的帖子发布后,我想再做一点挖掘。我写了一些C代码来看看matlab是如何创建一个零矩阵的。这是我使用的C++代码。< /P>
int main(int argc, char **argv)
{
    for (int i = 1975; i <= 2100; i+=25) {
    timer::start();
    double *foo = (double *)malloc(i * i * sizeof(double));
    for (int k = 0; k < i * i; k++) foo[k]  = 0;
    double mftime = timer::stop();
    free(foo);

    timer::start();
    double *bar = (double *)malloc(i * i * sizeof(double));
    memset(bar, 0, i * i * sizeof(double));
    double mmtime = timer::stop();
    free(bar);

    timer::start();
    double *baz = (double *)calloc(i * i, sizeof(double));
    double catime = timer::stop();
    free(baz);

    printf("%d, %lf, %lf, %lf\n", i, mftime, mmtime, catime);
    }
}
如您所见,calloc(第4列)似乎是最快的方法。从2025年到2050年,它的速度也显著加快(我想大概是2048年左右吧?)

现在我回到matlab去检查同样的问题。以下是结果

$ ./test
1975, 0.013812, 0.013578, 0.003321
2000, 0.014144, 0.013879, 0.003408
2025, 0.014396, 0.014219, 0.003490
2050, 0.014732, 0.013784, 0.000043
2075, 0.015022, 0.014122, 0.000045
2100, 0.014606, 0.014480, 0.000045
>> test
1975, 0.003296, 0.003297
2000, 0.003377, 0.003385
2025, 0.003465, 0.003464
2050, 0.015987, 0.000019
2075, 0.016373, 0.000019
2100, 0.016762, 0.000020

看起来f()和g()都在使用更小尺寸的
calloc
(在做了一些研究后,我在中发现,其中已经得出结论,使用
zero(M,N)
确实不是最有效的方法

他对
x=zeros(M,N)
clear x,x(M,N)=0进行计时,发现后者快约500倍。根据他的解释,第二种方法只是创建一个M-by-N矩阵,其元素自动初始化为0。然而,第一种方法创建
x
(使用具有自动零元素的
x
),然后再次为
x
中的每个元素分配一个零,这是一个需要更多时间的冗余操作


在空矩阵乘法的情况下,如您在问题中所示,MATLAB希望乘积为M×N矩阵,因此分配M×N矩阵。因此,输出矩阵自动初始化为零。由于原始矩阵为空,因此不执行进一步的计算,因此el输出矩阵中的元素保持不变并等于零。

有趣的问题是,显然有几种方法可以“击败”内置的
zero
函数。我对发生这种情况的唯一猜测是,它可以更高效地使用内存(毕竟,
zero(LargeNumer)
将更快地导致Matlab达到内存限制,而不是形成速度瓶颈(在大多数代码中),或者以某种方式更加健壮

这里是另一种使用稀疏矩阵的快速分配方法,我添加了常规零函数作为基准:

tic; x=zeros(1000,1000); toc
Elapsed time is 0.002863 seconds.

tic; clear x; x(1000,1000)=0; toc
Elapsed time is 0.000282 seconds.

tic; x=full(spalloc(1000,1000,0)); toc
Elapsed time is 0.000273 seconds.

tic; x=spalloc(1000,1000,1000000); toc %Is this the same for practical purposes?
Elapsed time is 0.000281 seconds.

结果可能有点误导。当您将两个空矩阵相乘时,生成的矩阵不会立即“分配”和“初始化”,而是推迟到您第一次使用它(有点像延迟计算)

当变量越界时也同样适用,在数值数组的情况下,它用零填充任何缺少的条目(我在后面讨论非数值情况)。当然,以这种方式增长矩阵不会覆盖现有元素

因此,虽然它看起来更快,但您只是将分配时间延迟到您实际第一次使用矩阵。最终,您将有类似的时间安排,就像您从一开始就进行分配一样

显示t的示例
tic; x=zeros(1000,1000); toc
Elapsed time is 0.002863 seconds.

tic; clear x; x(1000,1000)=0; toc
Elapsed time is 0.000282 seconds.

tic; x=full(spalloc(1000,1000,0)); toc
Elapsed time is 0.000273 seconds.

tic; x=spalloc(1000,1000,1000000); toc %Is this the same for practical purposes?
Elapsed time is 0.000281 seconds.
N = 1000;

clear z
tic, z = zeros(N,N); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))

clear z
tic, z = zeros(N,0)*zeros(0,N); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))

clear z
tic, z(N,N) = 0; toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))

clear z
tic, z = full(spalloc(N,N,0)); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))

clear z
tic, z(1:N,1:N) = 0; toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))

clear z
val = 0;
tic, z = val(ones(N)); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))

clear z
tic, z = repmat(0, [N N]); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
// zeros(N,N)
Elapsed time is 0.004525 seconds.
Elapsed time is 0.000792 seconds.

// zeros(N,0)*zeros(0,N)
Elapsed time is 0.000052 seconds.
Elapsed time is 0.004365 seconds.

// z(N,N) = 0
Elapsed time is 0.000053 seconds.
Elapsed time is 0.004119 seconds.
// full(spalloc(N,N,0))
Elapsed time is 0.001463 seconds.
Elapsed time is 0.003751 seconds.

// z(1:N,1:N) = 0
Elapsed time is 0.006820 seconds.
Elapsed time is 0.000647 seconds.

// val(ones(N))
Elapsed time is 0.034880 seconds.
Elapsed time is 0.000911 seconds.

// repmat(0, [N N])
Elapsed time is 0.001320 seconds.
Elapsed time is 0.003749 seconds.
N = 1000;

tic, a = cell(N,N); toc
tic, b = repmat({[]}, [N,N]); toc
tic, c{N,N} = []; toc
Elapsed time is 0.001245 seconds.
Elapsed time is 0.040698 seconds.
Elapsed time is 0.004846 seconds.
>> assert(isequal(a,b,c))
>> whos a b c
  Name         Size                  Bytes  Class    Attributes

  a         1000x1000              8000000  cell               
  b         1000x1000            112000000  cell               
  c         1000x1000              8000104  cell               
function compare_zeros_init()
    iter = 100;
    for N = 512.*(1:8)
        % ZEROS(N,N)
        t = zeros(iter,3);
        for i=1:iter
            clear z
            tic, z = zeros(N,N); t(i,1) = toc;
            tic, z(:) = 9; t(i,2) = toc;
            tic, z = z + 1; t(i,3) = toc;
        end
        fprintf('N = %4d, ZEROS = %.9f\n', N, mean(sum(t,2)))

        % z(N,N)=0
        t = zeros(iter,3);
        for i=1:iter
            clear z
            tic, z(N,N) = 0; t(i,1) = toc;
            tic, z(:) = 9; t(i,2) = toc;
            tic, z = z + 1; t(i,3) = toc;
        end
        fprintf('N = %4d, GROW  = %.9f\n', N, mean(sum(t,2)))

        % ZEROS(N,0)*ZEROS(0,N)
        t = zeros(iter,3);
        for i=1:iter
            clear z
            tic, z = zeros(N,0)*zeros(0,N); t(i,1) = toc;
            tic, z(:) = 9; t(i,2) = toc;
            tic, z = z + 1; t(i,3) = toc;
        end
        fprintf('N = %4d, MULT  = %.9f\n\n', N, mean(sum(t,2)))
    end
end
>> compare_zeros_init
N =  512, ZEROS = 0.001560168
N =  512, GROW  = 0.001479991
N =  512, MULT  = 0.001457031

N = 1024, ZEROS = 0.005744873
N = 1024, GROW  = 0.005352638
N = 1024, MULT  = 0.005359236

N = 1536, ZEROS = 0.011950846
N = 1536, GROW  = 0.009051589
N = 1536, MULT  = 0.008418878

N = 2048, ZEROS = 0.012154002
N = 2048, GROW  = 0.010996315
N = 2048, MULT  = 0.011002169

N = 2560, ZEROS = 0.017940950
N = 2560, GROW  = 0.017641046
N = 2560, MULT  = 0.017640323

N = 3072, ZEROS = 0.025657999
N = 3072, GROW  = 0.025836506
N = 3072, MULT  = 0.051533432

N = 3584, ZEROS = 0.074739924
N = 3584, GROW  = 0.070486857
N = 3584, MULT  = 0.072822335

N = 4096, ZEROS = 0.098791732
N = 4096, GROW  = 0.095849788
N = 4096, MULT  = 0.102148452