Ejecuto en paralelo un montón de procesos que consumen mucha CPU; normalmente utilizan unos pocos GB de memoria cada uno. De vez en cuando también asignan una gran cantidad de memoria (por ejemplo, entre 150 y 250 GB). Normalmente uno de los procesos lo hace como máximo, por lo que caben en la RAM disponible (384 GB en mi máquina). Sin embargo, a veces sucede que varios asignan esta gran cantidad al mismo tiempo y (obviamente) todo se ralentiza debido al intercambio.
En tales casos, detengo todos los procesos que consumen memoria menos uno, para permitir que se calcule de manera efectiva. Pero lleva mucho tiempo restablecer un proceso detenido, ya que significa cargar decenas de gigabytes desde el disco en un patrón de acceso aleatorio. De ahí la pregunta es: ¿cómo puedo forzar a un solo proceso a cargar secuencialmente todo el núcleo desde el intercambio?
Hasta ahora solo he encontrado una sugerencia de kernel de intercambio, que (con la ayuda de cgroups) puede evitar que un proceso intercambie más, pero no ayuda en el rendimiento del intercambio. Obviamente, no es posible desactivar todo el intercambio, ya que los otros procesos detenidos tienen que ocupar espacio allí.
Crear mi propio miniprogramador tampoco es una opción: los procesos son varios scripts/programas pequeños en Python y los picos de memoria generalmente ocurren en llamadas a la biblioteca, por lo que no puedo predecir cuándo ocurrirá un pico.
Sólo para dejar claro: no considero comprar terabytes de RAM, a esta escala es demasiado caro. Poner el intercambio en una matriz SSD/SSD ayudará solo un poco (medido), por lo que tampoco es la solución que estoy buscando.
(autorespuesta parcial):
Parece que la lectura de intercambio realmente secuencial (solo páginas que pertenecen a un solo proceso) es difícilmente posible sin piratear el kernel: medí swapoff -a
y ciertamente no leí el intercambio secuencialmente. Y sería lógico leerlo más rápido si dicha optimización fuera fácil de implementar.
Actualmente, mi mejor enfoque es leer toda la memoria del proceso a través de /proc/[pid]/mem
un pseudoarchivo usando el siguiente script (que debe ejecutarse como root):
#!/usr/bin/python2
import re
import sys
pid=str(sys.argv[1]) # process pid given by the first arg
print(pid) # just to aviod mistakes
CHUNKSIZE=10485760 # single read() invocation block size
total=0
maps_file = open("/proc/"+pid+"/maps", 'r')
mem_file = open("/proc/"+pid+"/mem", 'r', 0)
for line in maps_file.readlines(): # for each mapped region
m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
if m.group(3) == 'r': # if this is a readable region
start = int(m.group(1), 16)
end = int(m.group(2), 16)
mem_file.seek(start) # seek to region start
togo = end-start # number of bytes to read
while togo > CHUNKSIZE: # read sequential memory from region one block at the moment
mem_file.read(CHUNKSIZE)
togo -= CHUNKSIZE
total += CHUNKSIZE
print(total/1048576) # be verbose, print megabytes read so far
mem_file.read(togo) # read remaining region contents
total+=togo # dump contents to standard output
print(total/1048576) # be verbose...
maps_file.close()
mem_file.close()
Sucede que falla en los últimos bytes de memoria, pero generalmente funciona con el mismo rendimiento swapoff
y carga solo el proceso dado. El guión es un fragmento modificado deesta respuesta.