AnsiStrings + MultiThreading = NO-GO

Estoy implementando el que el sistema pueda hacer las búsquedas de las firmas acústicas dentro de las firmas de días ya existentes.

En promedio, se tarda como 800 milisegundos en buscar todas las ocurrencias (o las no-ocurrencias) de un spot de 20 segundos en un día de 24 horas. No muy mal :-). El problema viene cuando tienes que buscar, digamos, 400 ó 500 spots en 30 ó 40 grabaciones. Quick math: 40*500*800 = 16,000 segundos = 266 minutos = 4.4 horas. Ya no se oye tan bien, verdad?

Bueno, y qué sucede si dividimos este trabajo en varias tareas? En teoría, si tenemos N procesadores para dividir el trabajo, entonces sería 4.4 horas / N procesadores. 4 procesadores vendrían a ser 1.1 horas para todo el proceso. Lo cuál no está muy mal. Así que… segunda fase… implementar la búsqueda en multitareas.

Tenemos una clase básica para hacer la búsqueda/comparación de las firmas. En teoría, está preparada para trabajar en multitareas. No tiene accesos (o eso creía hace 24 horas) externos que puedan causar bloqueos, o cosas por el estilo, así que dividirlo en varias tareas no debería de ser problema. Qué equivocado estaba!

La mayoría de las clases y funciones que diseñé pasaban parámetros y resultados con objetos de tipo AnsiString. Quién ha trabajado con C++Builder sabrá que es una implementación de una clase para manejar strings. En teoría funciona a todo dar, y así es en la práctica en la mayoría de los casos… aunque por lo visto cuando se trata de multitareas no es lo más adecuado.

No estoy bien seguro de cuáles sean los detalles de implementación de esa clase. Nunca los he revisado. Lo que sí es claro es que, al poder manejar strings de cualquier tamaño, y poder cambiar strings de un tamaño a otro aún con una instancia de AnsiString ya generada, debe tener algo de manejo interno de memoria, etc. Qué sucede pues si tienes N clases haciendo miles de llamadas a funciones cuyos parámetros son AnsiStrings? El manejo de memoria se hace un desmadre!!! Cada llamada a función tiene que construir un AnsiString (o muchos!), con sus respectivas llamadas a asignar memoria, y destruirla al finalizar la función, y crear y destruir memoria en multitareas implica bloquear, aunque sea por un momentititito, a las demás tareas de hacer eso mismo.

Total… en vez de bajarle a 1/N el tiempo, nada más conseguimos un aumento de velocidad de… a lo mejor un 20%. Y en algunos otros casos (por ejemplo, corriendo estas rutinas en el servidor, con 8 procesadores) resulta que es hasta más tardado, o deplano una lástima ver 8 tareas esperando cada una a las otras.

Solución: remover todos los AnsiStrings de parámetros y resultados de esas funciones.

Después de eso tenemos esta pequeña imágen, que me llenó de emoción:

Qué significa el área verde, en CPU Usage History? Qué estamos usando los 4 procesadores de mi Mac Pro al 100%, es decir, que adios bloqueos entre las diferentes tareas, y bienvenido el aumento lineal de velocidad con respecto al número de procesadores disponibles!

Ahora nada más nos queda implementar el poder dividir este proceso, además de en varias tareas dentro de una misma instancia del sistema, en varias computadoras.

Hace algunas semanas o días, no recuerdo bien, salió un documento sobre la tecnología que usa Google para implementar la indexación y búsqueda de páginas. Obviamente, no tienen una sola máquina enorme haciendo todo el trabajo. Tienen miles y miles de máquinas relativamente sencillas, cada una atendiendo una pequeña parte de todo el trabajo. Hasta donde tengo entendido, todo se hace usando una tecnología… no recuerdo bien cuál es el nombre… MapReduce (después de 15 minutos de búsqueda por internet)… que permite distribuir estas pequeñas cargas de trabajo en muchas computadoras, y luego reunir los resultdados en una sola entidad. Les dejo la liga: http://en.wikipedia.org/wiki/MapReduce

Leave a Reply