Comprender el uso de memoria en python

Estoy tratando de entender cómo Python está usando la memoria para estimar cuántos procesos puedo ejecutar a la vez. En este momento, proceso archivos grandes en un servidor con grandes cantidades de RAM (~ 90-150GB de RAM libre).

Para una prueba, haría cosas en Python, luego miraría htop para ver cuál era el uso.

paso 1: abro un archivo de 2.55GB y lo guardo en una cadena

with open(file,'r') as f:
    data=f.read()

Uso es 2686M

paso 2: dividí el archivo en nuevas líneas

data = data.split('\n')

usage es 7476M

paso 3: solo mantengo cada 4ta línea (dos de las tres líneas que elimino tienen la misma longitud que la línea que mantengo)

data=[data[x] for x in range(0,len(data)) if x%4==1]

usage es 8543M

paso 4: dividí esto en 20 partes iguales para ejecutar un grupo de multiprocesamiento.

l=[] 
for b in range(0,len(data),len(data)/40):
    l.append(data[b:b+(len(data)/40)])

usage es 8621M

paso 5: borro datos,usage es 8496M.

Hay varias cosas que no tienen sentido para mí.

En el paso dos, ¿por qué el uso de la memoria aumenta tanto cuando cambio la cadena a una matriz? ¿Asumo que los contenedores de matriz son mucho más grandes que el contenedor de cadena?

en el paso tres, ¿por qué los datos no se reducen significativamente? Básicamente eliminé 3/4 de mis matrices y al menos 2/3 de los datos dentro de la matriz. Esperaría que se redujera en consecuencia. Llamar al recolector de basura no hizo ninguna diferencia.

oddly suficiente cuando asigné la matriz más pequeña a otra variable, usa menos memoria. @usage 6605M

cuando elimino el objeto antiguodata: usage 6059M

Esto me parece raro. Cualquier ayuda para reducir la huella de mi memoria sería apreciada.

EDITA

Está bien, esto me está haciendo doler la cabeza. Claramente, Python está haciendo algunas cosas raras detrás de escena aquí ... y solo Python. Hice el siguiente script para demostrar esto usando mi método original y el método sugerido en la respuesta a continuación. Los números están todos en GB.

CÓDIGO DE PRUEBA

import os,sys
import psutil
process = psutil.Process(os.getpid())
import time

py_usage=process.memory_info().vms / 1000000000.0
in_file = "14982X16.fastq"

def totalsize(o):
    size = 0
    for x in o:
        size += sys.getsizeof(x)
    size += sys.getsizeof(o)
    return "Object size:"+str(size/1000000000.0)

def getlines4(f):
    for i, line in enumerate(f):
        if i % 4 == 1:
            yield line.rstrip()

def method1():
    start=time.time()
    with open(in_file,'rb') as f:
        data = f.read().split("\n")
    data=[data[x] for x in xrange(0,len(data)) if x%4==1]
    return data

def method2():
    start=time.time()
    with open(in_file,'rb') as f:
        data2=list(getlines4(f))
    return data2


print "method1 == method2",method1()==method2()
print "Nothing in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data=method1()
print "data from method1 is in memory"
print "method1", totalsize(data)
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data
print "Nothing in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data2=method2()
print "data from method2 is in memory"
print "method2", totalsize(data2)
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data2
print "Nothing is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage


print "\nPrepare to have your mind blown even more!"
data=method1()
print "Data from method1 is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data2=method2()
print "Data from method1 and method 2 are in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
data==data2
print "Compared the two lists"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data
print "Data from method2 is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage
del data2
print "Nothing is in memory"
print "Usage:", (process.memory_info().vms / 1000000000.0) - py_usage

SALID

method1 == method2 True
Nothing in memory
Usage: 0.001798144
data from method1 is in memory
method1 Object size:1.52604683
Usage: 4.552925184
Nothing in memory
Usage: 0.001798144
data from method2 is in memory
method2 Object size:1.534815518
Usage: 1.56932096
Nothing is in memory
Usage: 0.001798144

Prepare to have your mind blown even more!
Data from method1 is in memory
Usage: 4.552925184
Data from method1 and method 2 are in memory
Usage: 4.692287488
Compared the two lists
Usage: 4.692287488
Data from method2 is in memory
Usage: 4.56169472
Nothing is in memory
Usage: 0.001798144

para aquellos de ustedes que usan python3 es bastante similar, excepto que no es tan malo después de la operación de comparación ...

SALIDA DE PYTHON3

method1 == method2 True
Nothing in memory
Usage: 0.004395008000000006
data from method1 is in memory
method1 Object size:1.718523294
Usage: 5.322555392
Nothing in memory
Usage: 0.004395008000000006
data from method2 is in memory
method2 Object size:1.727291982
Usage: 1.872596992
Nothing is in memory
Usage: 0.004395008000000006

Prepare to have your mind blown even more!
Data from method1 is in memory
Usage: 5.322555392
Data from method1 and method 2 are in memory
Usage: 5.461917696
Compared the two lists
Usage: 5.461917696
Data from method2 is in memory
Usage: 2.747633664
Nothing is in memory
Usage: 0.004395008000000006

moral de la historia ... la memoria para Python parece ser un poco como Camelot para Monty Python ... es un lugar muy tonto.

Respuestas a la pregunta(1)

Su respuesta a la pregunta