Почему 352 ГБ NumPy ndarray можно использовать на компьютере MacOS с 8 ГБ памяти?

import numpy as np

array = np.zeros((210000, 210000)) # default numpy.float64
array.nbytes

Когда я запускаю приведенный выше код на моем MacBook с 8 Гб памяти с macOS, ошибки не возникает. Но, выполняя тот же код на ПК с 16 ГБ памяти с Windows 10, или на ноутбуке Ubuntu с 12 ГБ памяти, или даже на суперкомпьютере Linux с 128 ГБ памяти, интерпретатор Python вызовет ошибку MemoryError. Во всех тестовых средах установлен 64-битный Python 3.6 или 3.7.

Ответы

Ответ 1

Ответ @Martijn Pieters находится на правильном пути, но не совсем правильно: это не имеет никакого отношения к сжатию памяти, но вместо этого оно имеет отношение к виртуальной памяти.

Например, попробуйте запустить на своем компьютере следующий код:

arrays = [np.zeros((21000, 21000)) for _ in range(0, 10000)]

Этот код выделяет 32 ТБ памяти, но вы не получите сообщение об ошибке (по крайней мере, в Linux). Если я проверю htop, я вижу следующее:

  PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
31362 user       20   0 32.1T 69216 12712 S  0.0  0.4  0:00.22 python

Это связано с тем, что ОС идеально подходит для виртуальной памяти. На самом деле он не будет назначать страницы физической памяти, пока не понадобится. Как это работает:

  • calloc запрашивает у ОС немного памяти для использования
  • ОС просматривает таблицы страниц процесса и находит кусок памяти, который она хочет назначить. Это быстрая операция, ОС просто сохраняет диапазон адресов памяти во внутренней структуре данных.
  • программа пишет по одному из адресов.
  • ОС получает ошибку страницы, после чего она просматривает и фактически назначает страницу физической памяти. Размер страницы обычно составляет несколько КиБ.
  • ОС передает управление обратно программе, которая продолжается, не замечая прерывания.

Создание одного огромного массива не работает в Linux, потому что по умолчанию применяется "эвристический алгоритм, чтобы выяснить, достаточно ли памяти доступно". (спасибо @Martijn Pieters!) Некоторые эксперименты на моей системе показывают, что для меня ядро не желает предоставлять более 0x3BAFFFFFF байт. Тем не менее, если я запускаю echo 1 | sudo tee/proc/sys/vm/overcommit_memory echo 1 | sudo tee/proc/sys/vm/overcommit_memory, а затем снова попробуйте программу в OP, она работает нормально.

Для развлечения попробуйте запустить arrays = [np.ones((21000, 21000)) for _ in range(0, 10000)]. Вы обязательно получите ошибку нехватки памяти, даже на MacO или Linux с компрессией подкачки. Да, некоторые операционные системы могут сжимать оперативную память, но они не могут сжимать ее до уровня, при котором вам не хватит памяти.