Можно ли использовать память жесткого диска вместо оперативной для переменных в коде?

Есть программа, которая принимает список xlsx файлов и прогоняет их построчно по очереди, достает нужные сведения и сопоставляет их между собой (ищет совпадения в файлах, сопоставляет их и так далее..) Выглядит примерно так:

key_info = {}
for file in tuple_of_files:
    wb = openpyxl.load_workbook(file, read_only=True)
    first_sheet = wb.sheetnames[0]
    for row in wb[first_sheet].rows:
        #тут фрагмент кода чтобы достать из строки нужный объект, который мы сохраняем в переменную var_info
        if var_info not in key_info:
            key_info[var_info] = {'count' : 1, 'reg' : None, 'oper' : None, 'time_use' : set(),  'imeis' : set()}
            #далее фрагмент кода по вычислению и заполнению подсловаря для этого объекта
        elif var_info in key_info:
            key_info[var_info]['count'] += 1
            key_info[var_info]['time_use'].add(some_info)
            key_info[var_info]['imeis'].add(some_other_info)

Потом используя словарь с ключевыми значениями key_info создаю xlsx файл и записываю в него результаты анализа. Программа работает, пока общее число строк в передаваемых ей файлах не превышает примерно 1млн, и в целом делает свою работу. Но когда пытаешься передать файлы с большим объемом (к примеру общее число строк около 5млн), то возникает ошибка memoryError. Я понимаю, что это из-за того, что словарь key_info просто уже не может вмещать информацию, однако разбить его на несколько других файлов или обнулять через определенное число строк, никак не получится - теряется весь функционал проверки.

Есть ли какой-нибудь способ сохранять этот словарь key_info не в оперативной памяти, а на жестком диске и работать с ним по той же схеме?

Пытался реализовать путем создания вместо словаря базу данных SQLite на жестком диске и работать с ней по аналогичному алгоритму, но тогда по мере ее наполнения скорость SELECT/UPDATE катастрофически падает и весь анализ занимает огромное количество времени. К примеру анализ 1млн строк с использованием словаря составляет в среднем 600 секунд, с использованием SQLite (для сравнения созданной как на жестком диске, так и в оперативной памяти) - 4800 секунд (и скорость падает еще сильнее по мере наполнения базы)...


Ответы (2 шт):

Автор решения: LeFroys

А результат в итоге идёт в какой-то файл excel? Можно было бы после каждой итерации обработки файла класть в результирующий excel и обнулять словарь, тем самым наращивая результирующий excel файл. Либо просто всё коммитить в БД и потом уже работать с ней, селект из БД в любом случае выполнится быстрее чем вы будете обрабатывать результирующий excel файл (если он имеется)

→ Ссылка
Автор решения: vadim vaduxa

Не уверен насколько это поможет, но я бы попробовал хранить все в БД + для ускорения хранить в кеше какое то кол-во записей из key_info, чтобы меньше дергать БД

Что то типа этого

class CacheKeyInfo:
    def __init__(self):
        self.max_contains = 100000  # размер кеша, чтобы не было memoryError
        self.key_info_cache = {}  # сам кеш
        self.key_info_contains = set()  # названия всех встречавшихся var_info

    def work(self, var_info):
        if var_info in self.key_info_cache:  # var_info есть, быстрый поиск из кеша
            self.key_info_cache[var_info]['count'] += 1
        elif var_info in self.key_info_contains:  # var_info есть, но лежит в БД
            if len(self.key_info_cache) > self.max_contains:
                self.drop_from_cache()  # дропнуть чтобы не было memoryError
            self.key_info_cache[var_info] = self.select_from_sqlite3(var_info)  # найти var_info в БД
            self.key_info_cache[var_info]['count'] += 1
        else:  # новый var_info
            self.drop_from_cache()
            self.key_info_cache[var_info] = {'count': 1, 'reg': None, 'oper': None, 'time_use': set(), 'imeis': set()}
            self.key_info_contains.add(var_info)

    def drop_from_cache(self):
        key = next(iter(self.key_info_cache))
        val = self.key_info_cache.pop(key)
        self.insert_to_sqlite3(key, val)
        return key, val

    def insert_to_sqlite3(self, key, val):  # положить var_info в БД

    def select_from_sqlite3(self, key):  # найти var_info в БД

ob = CacheKeyInfo()
for file in tuple_of_files:
    for row in wb[first_sheet].rows:
        var_info = ...
        ob.work(var_info)

А в качестве оптимизации вашего кода:

if var_info not in key_info:
    ...
elif var_info in key_info:
    ...

Следует заменить на

if var_info in key_info:
    ...
else:
    ...
→ Ссылка