Ответ 1
Это поведение не уникально для MATLAB. Фактически, MATLAB не имеет никакого контроля над ним, так как это вызывает Windows. Linux и MacOS показывают одинаковое поведение.
Я заметил это одно и то же в программе С много лет назад. Оказывается, это хорошо документированное поведение. Этот отличный ответ объясняет в деталях, как управление памятью работает в большинстве современных ОС (спасибо Amro за обмен ссылкой!). Прочтите, если у этого ответа недостаточно подробностей.
Во-первых, повторите эксперимент Андера в 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
Отображаются два столбца: RSS и VSZ. RSS означает "Размер резидентного набора", это объем физической памяти (ОЗУ), который использует программа. VSZ означает "Виртуальный размер", это размер виртуальной памяти, назначенный программе. Обе величины находятся в KiB.
Столбец VSZ показывает 4 GiB при запуске программы. Я не уверен, что это такое, это кажется сверху. Но значение растет после malloc
и снова после calloc
, оба раза с примерно 98 000 KiB (немного выше выделенных 1e8 байт).
Напротив, столбец RSS показывает увеличение только 16 KiB после того, как мы выделили 1e8 байт. После записи в половину массива мы используем более 5е7 байт используемой памяти, а после записи в полный массив у нас бит более 1 байта в байтах. Таким образом, память назначается по мере ее использования, а не когда мы сначала ее запрашиваем. Затем мы выделяем еще 1e8 байт с помощью calloc
и не видим изменений в RSS. Обратите внимание, что calloc
возвращает блок памяти, который инициализируется 0, точно так же, как и zeros
MATLAB.
Я говорю о calloc
потому что вполне вероятно, что zeros
MATLAB реализованы через calloc
.
Объяснение:
Современные компьютерные архитектуры разделяют виртуальную память (пространство памяти, которое видит процесс) из физической памяти. Процесс (т.е. программа) использует указатели для доступа к памяти, эти указатели являются адресами в виртуальной памяти. Эти адреса преобразуются системой в физические адреса при их использовании. Это имеет много преимуществ, например, невозможно, чтобы один процесс мог адресовать память, назначенную другому процессу, поскольку ни один из адресов, которые он может генерировать, никогда не будет переведен в физическую память, не назначенную этому процессу. Он также позволяет ОС обменивать память на холостом ходу, чтобы другой процесс использовал эту физическую память. Обратите внимание, что физическая память для непрерывного блока виртуальной памяти не обязательно должна быть смежной!
Ключ - выделенный жирным шрифтом текст выше: при использовании. Память, назначенная процессу, может фактически не существовать, пока процесс не попытается прочитать или записать на него. Вот почему мы не видим никаких изменений в RSS при распределении большого массива. Используемая память назначается физической памяти на страницах (блоки обычно 4 KiB, иногда до 1 MiB). Поэтому, когда мы пишем в один байт нашего нового блока памяти, назначается только одна страница.
Некоторые ОС, такие как Linux, будут даже "перекомпилировать" память. Linux будет присваивать больше виртуальной памяти процессам, чем имеет возможность помещать в физическую память, в предположении, что эти процессы не будут использовать всю память, которую они назначили в любом случае. Этот ответ расскажет вам больше об излишнем, чем вы захотите узнать.
Итак, что происходит с calloc
, который возвращает ноль-инициализированную память? Это также объясняется в ответе, который я связывал ранее. Для небольших массивов malloc
и calloc
возвращают блок памяти из большего пула, полученного из ОС в начале программы. В этом случае calloc
будет записывать нули во все байты, чтобы убедиться, что он инициализирован нулем. Но для больших массивов из ОС непосредственно получается новый блок памяти. ОС всегда выдает память, которая обнуляется (опять же, она не позволяет одной программе видеть данные из другой программы). Но поскольку память не получает физическое назначение до использования, обнуление также задерживается до тех пор, пока страница памяти не будет помещена в физическую память.
Вернуться к MATLAB:
Эксперимент выше показывает, что можно получить нулевой блок памяти -o ut в постоянное время и без изменения физического размера программной памяти. Это как MATLAB функция zeros
выделяет память, не вы видите какие - либо изменения в памяти след MATLAB.
Эксперимент также показывает, что zeros
выделяют полный массив (вероятно, через calloc
), и этот объем памяти увеличивается только при использовании этого массива, по одной странице за раз.
В предопределяющем совете MathWorks говорится, что
вы можете улучшить время выполнения кода, предварительно распределив максимальное пространство, необходимое для массива.
Если мы выделяем небольшой массив, то хотим увеличить его размер, нужно выделить новый массив и скопировать данные. Как массив связан с ОЗУ, это не влияет на это, MATLAB видит только виртуальную память, у него нет контроля (или даже знания?) О том, где хранятся эти данные в физической памяти (ОЗУ). Все, что имеет значение для массива с точки зрения MATLAB (или любой другой программы), состоит в том, что массив является непрерывным блоком виртуальной памяти. Расширение существующего блока памяти не всегда (обычно не?) Возможно, и поэтому получается новый блок и копируются данные. Например, см. График в этом другом ответе: когда массив увеличен (это происходит при больших вертикальных шипах) данные копируются; чем больше массив, тем больше данных нужно скопировать.
Предораспределение позволяет избежать увеличения массива, поскольку мы делаем его достаточно большим для начала. Фактически, более эффективно создавать массив, который слишком велик для того, что нам нужно, поскольку часть массива, который мы не используем, на самом деле никогда не передается программе. То есть, если мы выделяем очень большой блок виртуальной памяти и используем только первые 1000 элементов, мы будем использовать только несколько страниц физической памяти.
Поведение calloc
описанное выше, объясняет также это другое странное поведение функции zeros
: для небольших массивов zeros
дороже, чем для больших массивов, потому что небольшие массивы должны быть обнулены явно программой, тогда как большие массивы неявно обнуляются ОПЕРАЦИОННЫЕ СИСТЕМЫ.