Стоит задача построчных манипуляций с большим файлом. Пробовал разные подходы. Оказалось, что попытки работы напрямую с файлом упираются в кэш файловой системы. Все эти tell-ы и seek-и выполняются быстро только если файл целиком влазит в этот самый кэш.
Поразмыслив я решил не увеличивать размер кэша. Во первых, память нужна не только этому скрипту. Во вторых работа с даже полностью закэшированным файлом всё же на порядок медленнее, чем с памятью напрямую. В общем, решил загонять весь файл в память.
Первое, что по этому поводу выяснилось, стандартный list питона кушает памяти в 2 раза больше, чем содержит данных. Я тестил на строчках требуемой длины (27 байт).
Вообще, мне хотелось бы даже сжать данные. Потому попробовал я записать bz2 файлик в tmpfs. Это ведь тоже работа с памятью. А потом считывать из него нужные строчки, там тоже можно сики делать. Это оказалось просто невероятно медленно.
Потом посмотрел в сторону mmap. Даже если просто в память через неё писать, работает раза в два дольше, чем с закэшированным файлом. Либо кто-то из нас тормоз, либо одно из двух :)
Остановился на array. То, что получилось работает на скорости сравнимой с стандартным list-ом. Вот оно, собственно:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import array
def main():
# тестим
import random
a = StringList()
a.append(u'Утол')
a.append(u'Мар')
print len(a)
print a[1].encode('utf-8')
print random.choice(a).encode('utf-8')
class StringList(object):
'''
Экономичный к памяти список строк
'''
def __init__(self, encoding='cp1251'):
self.data = array.array('c')
self.index = array.array('L')
self.encoding = encoding
def append(self, s):
self.index.append(len(self.data))
self.data.fromstring(s.encode(self.encoding))
def __getitem__(self, id):
start = self.index[id]
try:
end = self.index[id + 1]
except IndexError:
end = len(self.data)
return self.data[start:end].tostring().decode(self.encoding)
def __len__(self):
return len(self.index)
if __name__ == "__main__":
main()
0 коммент.:
Отправить комментарий