Я подозреваю, что проблема в том, что у вас столько данных, которые хранятся в строковом формате, что действительно бесполезно для вашего случая использования, что у вас заканчивается реальная память и происходит перестановка. 128 ГБ должно быть достаточно , чтобы этого избежать ...:)
Поскольку вы указали в комментариях, что вам все равно нужно хранить дополнительную информацию, отдельный класс, который ссылается народительская строка будет моим выбором.Я провел короткий тест, используя chr21.fa из chromFa.zip из hg18;файл составляет около 48 МБ и чуть менее 1 млн. строк.У меня здесь только 1 ГБ памяти, поэтому я просто выбрасываю объекты потом.Таким образом, этот тест не будет показывать проблемы с фрагментацией, кэшем или другими, но я думаю, что он должен быть хорошей отправной точкой для измерения пропускной способности парсинга:
import mmap
import os
import time
import sys
class Subseq(object):
__slots__ = ("parent", "offset", "length")
def __init__(self, parent, offset, length):
self.parent = parent
self.offset = offset
self.length = length
# these are discussed in comments:
def __str__(self):
return self.parent[self.offset:self.offset + self.length]
def __hash__(self):
return hash(str(self))
def __getitem__(self, index):
# doesn't currently handle slicing
assert 0 <= index < self.length
return self.parent[self.offset + index]
# other methods
def parse(file, size=8):
file.readline() # skip header
whole = "".join(line.rstrip().upper() for line in file)
for offset in xrange(0, len(whole) - size + 1):
yield Subseq(whole, offset, size)
class Seq(object):
__slots__ = ("value", "offset")
def __init__(self, value, offset):
self.value = value
self.offset = offset
def parse_sep_str(file, size=8):
file.readline() # skip header
whole = "".join(line.rstrip().upper() for line in file)
for offset in xrange(0, len(whole) - size + 1):
yield Seq(whole[offset:offset + size], offset)
def parse_plain_str(file, size=8):
file.readline() # skip header
whole = "".join(line.rstrip().upper() for line in file)
for offset in xrange(0, len(whole) - size + 1):
yield whole[offset:offset+size]
def parse_tuple(file, size=8):
file.readline() # skip header
whole = "".join(line.rstrip().upper() for line in file)
for offset in xrange(0, len(whole) - size + 1):
yield (whole, offset, size)
def parse_orig(file, size=8):
file.readline() # skip header
buffer = ''
for line in file:
buffer += line.rstrip().upper()
while len(buffer) >= size:
yield buffer[:size]
buffer = buffer[1:]
def parse_os_read(file, size=8):
file.readline() # skip header
file_size = os.fstat(file.fileno()).st_size
whole = os.read(file.fileno(), file_size).replace("\n", "").upper()
for offset in xrange(0, len(whole) - size + 1):
yield whole[offset:offset+size]
def parse_mmap(file, size=8):
file.readline() # skip past the header
buffer = ""
for line in file:
buffer += line
if len(buffer) >= size:
for start in xrange(0, len(buffer) - size + 1):
yield buffer[start:start + size].upper()
buffer = buffer[-(len(buffer) - size + 1):]
for start in xrange(0, len(buffer) - size + 1):
yield buffer[start:start + size]
def length(x):
return sum(1 for _ in x)
def duration(secs):
return "%dm %ds" % divmod(secs, 60)
def main(argv):
tests = [parse, parse_sep_str, parse_tuple, parse_plain_str, parse_orig, parse_os_read]
n = 0
for fn in tests:
n += 1
with open(argv[1]) as f:
start = time.time()
length(fn(f))
end = time.time()
print "%d %-20s %s" % (n, fn.__name__, duration(end - start))
fn = parse_mmap
n += 1
with open(argv[1]) as f:
f = mmap.mmap(f.fileno(), 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
start = time.time()
length(fn(f))
end = time.time()
print "%d %-20s %s" % (n, fn.__name__, duration(end - start))
if __name__ == "__main__":
sys.exit(main(sys.argv))
1 parse 1m 42s
2 parse_sep_str 1m 42s
3 parse_tuple 0m 29s
4 parse_plain_str 0m 36s
5 parse_orig 0m 45s
6 parse_os_read 0m 34s
7 parse_mmap 0m 37s
Первые четыре - мой код, тогда как orig - ваш, а последние два - из других ответов здесь.
Пользовательские объекты гораздо дороже создавать и собирать, чем кортежи или простые строки!Это не должно вызывать удивления, но я не осознавал, что это будет иметь большое значение (сравните № 1 и № 3, которые на самом деле отличаются только в определяемом пользователем классе против кортежа).Вы сказали, что хотите сохранить дополнительную информацию, такую как смещение, со строкой в любом случае (как в случаях parse и parse_sep_str), поэтому вы можете рассмотреть возможность реализации этого типа в модуле расширения C.Посмотрите на Cython и связанные с ним, если вы не хотите писать C напрямую.
Ожидается, что случаи # 1 и # 2 будут идентичными: при указании на родительскую строку я пытался сэкономить память, а не время обработки, но этот тест не измеряет это.