Можно ли использовать память жесткого диска вместо оперативной для переменных в коде?
Есть программа, которая принимает список 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 шт):
А результат в итоге идёт в какой-то файл excel? Можно было бы после каждой итерации обработки файла класть в результирующий excel и обнулять словарь, тем самым наращивая результирующий excel файл. Либо просто всё коммитить в БД и потом уже работать с ней, селект из БД в любом случае выполнится быстрее чем вы будете обрабатывать результирующий excel файл (если он имеется)
Не уверен насколько это поможет, но я бы попробовал хранить все в БД + для ускорения хранить в кеше какое то кол-во записей из 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:
...