На самом деле это вовсе не утечка памяти, а оптимизация NumPy или ваша ОС дает вам. (Скорее всего, ваша ОС, учитывая наличие спецификаций ОС c в том, как ваш код работал в исходном вопросе.)
Вот упрощенный example.py
- основная идея c такая же.
import numpy as np
import os
import psutil
process = psutil.Process(os.getpid())
def print_memory(message):
rss = process.memory_info().rss
print(f"{message:30s} {rss // 1024:7d}kb")
M = N = 1000
print_memory('start')
#mymat = np.zeros((M, M, N), dtype=bool)
#mymat = (np.random.random((M, M, N)) > 0.5)
print_memory('generated')
for c1 in range(M):
if c1 % 100 == 0:
print_memory(f'iteration {c1}')
for c2 in range(M):
mymat[c1, c2, [1, 2, 3]] = True
print_memory(f'end')
print(mymat.nbytes, mymat.dtype, mymat.shape)
Выполнение этого с включенной первой mymat
строкой (т. Е. Генерирование нулей) печатает
$ python3 example.py
start 23552kb
generated 23564kb
iteration 0 23564kb
iteration 100 121340kb
iteration 200 218996kb
iteration 300 316652kb
iteration 400 414320kb
iteration 500 511976kb
iteration 600 609632kb
iteration 700 707288kb
iteration 800 804944kb
iteration 900 902600kb
end 1000256kb
1000000000 bool (1000, 1000, 1000)
, так что вы можете видеть, что «генерация» массива 1000 x 1000 x 1000 не на самом деле не занимают 1 гигабайт памяти Numpy nbytes
говорит, что это так, но когда он начинает заполняться ненулевыми данными, происходит распределение.
Запуск его со вторым параметром , генерируя случайные данные, выдает это:
$ python3 example.py
start 23404kb
generated 1000004kb
iteration 0 1000004kb
iteration 100 1000140kb
iteration 200 1000148kb
iteration 300 1000164kb
iteration 400 1000164kb
iteration 500 1000164kb
iteration 600 1000164kb
iteration 700 1000164kb
iteration 800 1000164kb
iteration 900 1000164kb
end 1000164kb
1000000000 bool (1000, 1000, 1000)
Как видите, вся память выделяется сразу, и то, что небольшое увеличение памяти происходит во время l oop, является практически ошибкой округления и, вероятно, просто из-за регулярных распределений G C. (np.ones()
имеет такое же поведение.)
Что касается вашего исходного массива 1000 *1000* 18000, если вам действительно нужно заполнить весь массив, вам в конечном итоге понадобится
>>> np.zeros((1000, 1000, 18000), dtype=bool).nbytes
18000000000
18 гигабайт памяти. (Это также указывает на тот факт, что Numpy не имеет определенного типа упакованных данных c для логических массивов; каждый логический тип занимает байт, а не только один бит. Это, вероятно, связано с соображениями производительности.)