Как я могу эффективно читать и записывать слишком большие файлы для размещения в памяти?
Я пытаюсь вычислить косинус-подобие 100 000 векторов, и каждый из этих векторов имеет 200 000 измерений.
Из чтения других вопросов я знаю, что memmap, PyTables и h5py - мои лучшие ставки для обработки таких данных, и я в настоящее время работает с двумя memmaps; один для считывания векторов, другой для хранения матрицы косинусных сходств.
Вот мой код:
import numpy as np
import scipy.spatial.distance as dist
xdim = 200000
ydim = 100000
wmat = np.memmap('inputfile', dtype = 'd', mode = 'r', shape = (xdim,ydim))
dmat = np.memmap('outputfile', dtype = 'd', mode = 'readwrite', shape = (ydim,ydim))
for i in np.arange(ydim)):
for j in np.arange(i+1,ydim):
dmat[i,j] = dist.cosine(wmat[:,i],wmat[:,j])
dmat.flush()
В настоящее время htop сообщает, что я использую 224G памяти VIRT и 91.2G из памяти RES, которая неуклонно растет. Мне кажется, что к концу процесса вся матрица вывода будет сохранена в памяти, чего я пытаюсь избежать.
Вопрос:
Является ли это правильным использованием memmaps, я пишу в выходной файл в эффективном режиме памяти (под этим я подразумеваю, что в памяти хранятся только необходимые части файлов ввода и вывода, т.е. dmat[i,j]
и wmat[:,i/j]
)?
Если нет, что я сделал не так, и как я могу это исправить?
Спасибо за любой совет, который у вас есть.
EDIT: Я только понял, что htop сообщает об общей загрузке системной памяти на 12G, так что кажется, что это работает в конце концов... кто-нибудь, кто может просветить меня? RES теперь находится на 111G...
EDIT2: memmap создается из массива 1D, состоящего из лотов и много длинных десятичных знаков, близких к 0, которые имеют нужный размер. Затем memmap выглядит следующим образом.
memmap([[ 9.83721223e-03, 4.42584107e-02, 9.85033578e-03, ...,
-2.30691545e-07, -1.65070799e-07, 5.99395837e-08],
[ 2.96711345e-04, -3.84307391e-04, 4.92968462e-07, ...,
-3.41317722e-08, 1.27959347e-09, 4.46846438e-08],
[ 1.64766260e-03, -1.47337747e-05, 7.43660202e-07, ...,
7.50395136e-08, -2.51943163e-09, 1.25393555e-07],
...,
[ -1.88709000e-04, -4.29454722e-06, 2.39720287e-08, ...,
-1.53058717e-08, 4.48678211e-03, 2.48127260e-07],
[ -3.34207882e-04, -4.60275148e-05, 3.36992876e-07, ...,
-2.30274532e-07, 2.51437794e-09, 1.25837564e-01],
[ 9.24923862e-04, -1.59552854e-03, 2.68354822e-07, ...,
-1.08862665e-05, 1.71283316e-07, 5.66851420e-01]])
Ответы
Ответ 1
С точки зрения использования памяти, нет ничего особенно плохого в том, что вы делаете в данный момент. Мембранные массивы обрабатываются на уровне ОС - данные, которые должны быть записаны, обычно хранятся во временном буфере и фиксируются только на диске, когда ОС сочтет это необходимым. Ваша ОС никогда не должна позволять вам исчерпывать физическую память, прежде чем очищать буфер записи.
Я бы посоветовал не называть flush
на каждой итерации, так как это побеждает цель позволить вашей ОС решить, когда писать на диск, чтобы максимизировать эффективность. На данный момент вы пишете только отдельные значения поплавка за раз.
С точки зрения эффективности ввода-вывода и ЦП, работа на одной линии за раз почти наверняка субоптимальна. Считывание и запись, как правило, быстрее для больших непрерывных блоков данных, а также ваши вычисления, вероятно, будут намного быстрее, если вы сможете обрабатывать сразу несколько строк с помощью векторизации. Общее эмпирическое правило состоит в том, чтобы обрабатывать как большой кусок вашего массива, который будет вписываться в память (включая любые промежуточные массивы, созданные во время вычислений).
Вот пример, показывающий, сколько вы можете ускорить операции над memmapped массивами, обработав их в кусках соответствующего размера.
Еще одна вещь, которая может иметь огромное значение, - это макет памяти ваших входных и выходных массивов. По умолчанию np.memmap
дает массив C-смежных (ряд строк). Поэтому доступ к столбцу wmat
по столбцам будет очень неэффективным, поскольку вы обращаетесь к несмежным местоположениям на диске. Вам было бы намного лучше, если wmat
был F-смежным (большой столбцом) на диске или если вы обращались к нему по строке.
Те же общие рекомендации относятся к использованию HDF5 вместо memmaps, хотя имейте в виду, что с HDF5 вам придется самому управлять всем управлением памятью.
Ответ 2
Карты памяти - это именно то, что имя говорит: сопоставление (виртуальных) дисковых секторов на страницах памяти. Память управляется операционной системой по требованию. Если памяти достаточно, система хранит части файлов в памяти, может быть, заполняет всю память, если их недостаточно, система может отбрасывать страницы, считанные из файла, или может менять их в пространство подкачки. Обычно вы можете полагаться на ОС как можно более эффективно.