Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización: 15 de marzo de 2008 - Sábado
El sistema de persistencia que he desplegado hace un mes funciona a la perfección, conforme a lo esperado. Pero he detectado algunas áreas mejorables en el uso concreto de la persistencia en este proyecto.
El principal de ellos es el rendimiento.
Tengo varias sesiones de BitTornado abiertas constantemente, algunas de las cuales con más de 500 torrents. Si recalculamos la pantalla una vez por segundo y tenemos 500 torrents, estamos haciendo unas 1000 transacciones por segundo. La contabilidad de tráfico para cada torrent supone dos transacciones: la primera para sincronizar la caché persistente local, y la segunda para actualizar los datos.
Debido a ello todos los procesos me estaban consumiendo el 27% de la CPU Una máquina antigua, eso sí: Pentium 4 a 1.4 Ghz, de 2001, con 640MB de RAM. la máquina no se dedica a nada más, así que no es un problema incluso en un caso extremo como éste (miles de torrents).
La cuestión es que el consumo de CPU es proporcional al número de torrents en el sistema, cuando debería ser proporcional al número de torrents activos en cada momento (o, más concretamente, proporcional al tráfico cursado). Esto es especialmente importante porque, en general, el número de torrents activos en un momento dado es muy bajo comparado con el número de torrents existentes.
La solución es bastante simple: refrescamos la caché local de persistencia una vez por segundo, no una vez por torrent existente en el sistema. Adicionalmente, hacemos "commit" solo cuando hay cambios.
Conforme a este nuevo diseño, si no hay actividad sólo se realiza una sincronización por segundo, independientemente del número de torrents en el sistema. Y si hay actividad, el tráfico de persistencia será proporcional al tráfico P2P (típicamente un "commit" cada 16 Kbytes). Con estos cambios, el consumo de CPU ha pasado del 27% a ser inapreciable.
Los cambios son:
--- BitTornado/CurrentRateMeasure.py (revision 7) +++ BitTornado/CurrentRateMeasure.py (working copy) @@ -3,7 +3,7 @@ from clock import clock -from monitor import monitor +from monitor import monitor_opt from time import time @@ -16,18 +16,18 @@ self.infohash=infohash self.up=0 if up else 1 - @monitor + @monitor_opt def x(conn,infohash,up) : speed=conn.get_root()["BT"] if infohash not in speed : speed[infohash]=[0,0,time()] grand_total=speed[infohash][up] - return grand_total + return (True,grand_total) self.total=x(self.infohash,self.up) def update_rate(self, amount): - @monitor + @monitor_opt def x(conn,infohash,up,amount) : speed=conn.get_root()["BT"] x=speed[infohash] @@ -36,7 +36,8 @@ x[up]=total x[2]=time() speed[infohash]=x - return total + return (True,total) + return (False,total) self.total=x(self.infohash,self.up,amount) Index: BitTornado/monitor.py =================================================================== --- BitTornado/monitor.py (revision 7) +++ BitTornado/monitor.py (working copy) @@ -3,6 +3,12 @@ persistencia_mutex=threading.Lock() +visualizar_mensaje=None + +def set_display(handler) : + global visualizar_mensaje + visualizar_mensaje=handler + def conecta_storage() : from durus.client_storage import ClientStorage from durus.connection import Connection @@ -12,39 +18,66 @@ try : persistencia = conecta_storage() break + except KeyboardInterrupt : + raise except : time.sleep(0.1) -def monitor(func) : +last_durus_sync=0 + +def monitor_opt(func) : def _monitor(*args, **kwargs) : global persistencia global persistencia_mutex + global last_durus_sync from durus.error import ConflictError - import socket + import socket,time + must_reconnect=False while True : # Reintenta si hay conflictos persistencia_mutex.acquire() try : # Nos aseguramos de liberar el lock + t=time.time() + if must_reconnect : + must_reconnect=False + if visualizar_mensaje!=None : + visualizar_mensaje("PROBLEMAS CON LA CONEXION DURUS... ABRIENDO UNA CONEXION NUEVA...") + last_durus_sync=t + while True : + try : + persistencia = conecta_storage() + break + except KeyboardInterrupt : + raise + except : + time.sleep(0.1) + if visualizar_mensaje!=None : + visualizar_mensaje("Conexion DURUS recuperada.") try : # Conflictos - + t=time.time() try : # Nos aseguramos de estar conectados - persistencia.abort() # Hacemos limpieza de cache + if t-last_durus_sync>1.0 : + last_durus_sync=t + persistencia.abort() # Hacemos limpieza de cache except socket.error : - import sys - import time - print >>sys.stderr,"PROBLEMAS CON LA CONEXION DURUS... ABRIENDO UNA CONEXION NUEVA" - while True : - try : - persistencia = conecta_storage() - break - except : - time.sleep(0.1) + must_reconnect=True + continue # Vuelve a intentarlo ret=func(persistencia,*args, **kwargs) - persistencia.commit() - return ret + if ret[0] : + last_durus_sync=time.time() + try : + persistencia.commit() + except socket.error : + must_reconnect=True + continue # Vuelve a intentarlo + + return ret[1] + except ConflictError : + last_durus_sync=0 pass # El abort ya se hace en el bucle except : + last_durus_sync=t persistencia.abort() import sys import time @@ -55,13 +88,14 @@ return _monitor -@monitor +@monitor_opt def inicializa(conn) : from durus.btree import BTree from durus.persistent_dict import PersistentDict root=conn.get_root() if "BT" not in root : root["BT"]=BTree() + return (True,None) inicializa() Index: btlaunchmanycurses.py =================================================================== --- btlaunchmanycurses.py (revision 6) +++ btlaunchmanycurses.py (working copy) @@ -273,7 +273,10 @@ def LaunchManyWrapper(scrwin, config): - LaunchMany(config, CursesDisplayer(scrwin)) + ventana=CursesDisplayer(scrwin) + from BitTornado import monitor + monitor.set_display(ventana.message) + LaunchMany(config, ventana) if __name__ == '__main__':
Detalles:
No pasa nada, porque el sistema (a través del módulo "monitor") está preparado para detectar este hecho y reintentar la operación por segunda vez.
En cualquier caso son mejoras a considerar para una versión futura.
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS