代码之家  ›  专栏  ›  技术社区  ›  Ander Biguri

我们需要预先分配。但是MATLAB不预先分配?

  •  13
  • Ander Biguri  · 技术社区  · 6 年前

    any() 短路(确实如此!)我发现了以下有趣的行为 preallocating 测试变量:

    test=zeros(1e7,1);
    >> tic;any(test);toc
    Elapsed time is 2.444690 seconds.
    >> test(2)=1;
    >> tic;any(test);toc
    Elapsed time is 0.000034 seconds.
    

    但是,如果我这样做了:

    test=ones(1e7,1);
    test(1:end)=0;
    tic;any(test);toc
    Elapsed time is 0.642413 seconds.
    >> test(2)=1;
    >> tic;any(test);toc
    Elapsed time is 0.000021 seconds.
    

    结果发现,这是因为变量在完全充满信息之前并不真正在RAM上,因此第一次测试需要更长的时间,因为它需要分配它。我检查的方法是查看Windows任务管理器中使用的内存。

    test=zeros(1e7,1);
    
    for ii=1:1e7
        test(ii)=1;
        if ii==1e7/2
            pause
        end
    end
    

    在检查MATLAB使用的内存时,我可以看到当停止时,它只使用了 test 需要内存(如果已满)。这可以用不同百分比的内存非常稳定地复制。

    有趣的是,下面也不分配整个矩阵。

    test=zeros(1e7,1);
    test(end)=1;
    

    我知道MATLAB并没有动态地分配和增加 测试 在循环中,因为这将使结束迭代非常慢(由于需要大量的memcopy),而且它还将在我提出的最后一个测试中分配整个数组。所以我的问题是:

    怎么回事?

    有人认为这可能与虚拟内存和物理内存有关,也与操作系统如何看待内存有关。但不确定这与这里提出的第一个测试有何联系。任何进一步的解释都是理想的。

    Win 10 x64,MATLAB 2017a软件

    1 回复  |  直到 6 年前
        1
  •  13
  •   Cris Luengo    6 年前

    这种行为不是MATLAB独有的。事实上,MATLAB无法控制它,因为它是Windows造成的。Linux和MacOS表现出相同的行为。

    This excellent answer 详细解释了内存管理在大多数现代操作系统中是如何工作的(谢谢 Amro 共享链接!)。如果这个答案没有足够的细节给你看。

    首先,让我们重复Ander在C语言中的实验:

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int main (void) {
    
       const int size = 1e8;
    
       /* For Linux: */
       // const char* ps_command = "ps --no-headers --format \"rss vsz\" -C so";
       /* For MacOS: */
       char ps_command[128];
       sprintf(ps_command, "ps -o rss,vsz -p %d", getpid());
    
       puts("At program start:");
       system(ps_command);
    
       /* Allocate large chunck of memory */
    
       char* mem = malloc(size);
    
       puts("After malloc:");
       system(ps_command);
    
       for(int ii = 0; ii < size/2; ++ii) {
          mem[ii] = 0;
       }
    
       puts("After writing to half the array:");
       system(ps_command);
    
       for(int ii = size/2; ii < size; ++ii) {
          mem[ii] = 0;
       }
    
       puts("After writing to the whole array:");
       system(ps_command);
    
       char* mem2 = calloc(size, 1);
    
       puts("After calloc:");
       system(ps_command);
    
       free(mem);
       free(mem2);
    }
    

    上面的代码适用于与POSIX兼容的操作系统(即除Windows以外的任何操作系统),但是在Windows上可以使用 Cygwin 使(大部分)符合POSIX。你可能需要改变 ps gcc so.c -o so ,与一起运行 ./so . 我在MacOS上看到以下输出:

    At program start:
       RSS      VSZ
       800  4267728
    After malloc:
       RSS      VSZ
       816  4366416
    After writing to half the array:
       RSS      VSZ
     49648  4366416
    After writing to the whole array:
       RSS      VSZ
     98476  4366416
    After calloc:
       RSS      VSZ
     98476  4464076
    

    VSZ列在程序启动时显示4 GiB。我不知道那是怎么回事,好像是过头了。但价值在 malloc 之后 calloc ,两次都有大约98000 KiB(略高于我们分配的1e8字节)。

    分配 ,并且在RSS中没有看到任何更改。请注意 分配 zeros 做。

    我说的是 分配 是通过 .

    现代计算机体系结构 虚拟存储器 (进程看到的内存空间)从 . 进程(即程序)使用指针访问内存,这些指针是虚拟内存中的地址。这些地址由系统转换为物理地址 使用时

    关键是上面的粗体斜体文本: 使用时 . 分配给进程的内存可能不存在,直到进程尝试读取或写入它。这就是为什么在分配大数组时,RSS没有任何变化。使用的内存分配给页面中的物理内存(块通常为4kib,有时可达1mib)。因此,当我们写入新内存块的一个字节时,只分配一个页。

    有些操作系统,比如Linux,甚至会“过度使用”内存。Linux将分配给进程的虚拟内存比它能够放入物理内存的容量还要多,前提是这些进程不会使用分配给它们的所有内存。 This answer 会告诉你比你想知道的还要多的过度努力。

    分配 ,它返回零初始化内存?这也在 the answer I linked earlier 马洛克 分配 从程序开始时从操作系统获得的较大池返回一块内存。在这种情况下, 将向所有字节写入零以确保初始化为零。但是对于更大的数组,一个新的内存块是直接从操作系统获得的。操作系统总是输出归零的内存(同样,它防止一个程序看到另一个程序的数据)。但由于内存在使用前不会被物理分配,因此归零也会延迟,直到内存页被放入物理内存。

    回到MATLAB:

    分配内存而不会看到MATLAB内存占用的任何变化。

    分配 ),并且该内存占用仅随使用此数组而增加,一次一页。

    The preallocation advice by the MathWorks

    通过预先分配数组所需的最大空间,可以提高代码执行时间。

    如果我们分配一个小数组,然后想要增加它的大小,就必须分配一个新数组并复制数据。数组与RAM的关联方式对这个没有影响,MATLAB只看到虚拟内存,它没有控制权(甚至没有知识?)这些数据存储在物理内存(RAM)中的位置。从MATLAB(或任何其他程序)的角度来看,对于数组来说,最重要的是数组是一个连续的虚拟内存块。扩大现有的内存块并不总是(通常不是?)可能,因此获得一个新块并复制数据。例如,请参见 the graph in this other answer :当阵列放大时(这种情况发生在较大的垂直峰值处),将复制数据;阵列越大,需要复制的数据越多。

    行为 分配 上面的描述也解释了 this other strange behavior of the zeros function :对于小阵列,