martes, 28 de agosto de 2012

Pasos para optimizar un programa

Este artículo pertenece a Referencias de programación

Hola a todos,

Hace semanas que tengo este artículo medio acabado , pero por unas cosas y otras nunca tengo tiempo de postearlo. Os pido  disculpas.

La idea de este post proviene de las preguntas por mail de un par de lectores que me comentaban  que hiciera un tutorial de programación de sockets (programación en C/C++ con comunicaciones por red) para un tema de optimización de velocidad en una aplicación de cálculo intensivo que estaban montando. Me fui mirando lo que tenían y llegamos a la conclusión de que realmente no les hacía falta implementar sockets, sino que simplemente con una buena gestión de código tenían suficiente.

Esta batallita me dió pie a hacer este artículo. "Pasos para optimizar un programa"  (tranquilos, el siguiente artículo es sobre sockets :D )

Cuando nos enfrentamos a un problema en que la velocidad es importante (en muchas ocasiones imprescindible) es muy usual en los programadores no demasiado experimentados buscar maneras muy originales y tecnológicas (aunque poco efectivas) para solventar el problema. Si esto os ha pasado, seguid estos pasos y no os volverá a pasar.

1. Mejorar el algoritmo: Lo primero de todo es mejorar el algoritmo. Por mucho que uséis calculo paralelo, con muchas CPU's y muchos cores y utilicéis todos los trucos que conozcáis, un mal algoritmo es un mal algoritmo, y a medida que el problema se hace grande, es lo que más pesa. Conclusión, antes que hacer aseguraos que aunque la implementación no sea optima, como mínimo el algoritmo sea el más adecuado a vuestro problema.  Si buscáis por internet encontrareis infinidad de algoritmos que de bien seguro cubrirán vuestras necesidades.

2. Mejorar la implementación: Si mejorando el algoritmo no tenemos suficiente para nuestros requisitos, siempre podemos usar los recursos que nos brinde el lenguaje (en nuestro caso C) para sacarle más jugo a nuestro equipo. Eliminar los if's dentro de lo posible, ordenarlos para que se ejecuten el mínimo de veces necesario, minimizar el número de bucles anidados (for, while ,etc...), evitar los accesos a memoria cuanto más mejor, etc... Todos estos trucos dependen del lenguaje , arquitectura y experiencia del programador. Por internet podreis encontrar algunos, otros los ireís viendo a medida que vayais programando.

3. Implementar trozos en ensamblador: Si tenemos el mejor algoritmo posible, implementado de la mejor manera posible, e incluso así no tenemos suficiente, tenemos que empezar a sacar la artillería. El ensamblador, (assembler o ASM en inglés) es un lenguaje de programación que se parece más al que usa el procesador que no un lenguaje como C/C++ o Java y por tanto proporciona más rendimiento (y aumenta la complejidad). Debido a la complejidad de este lenguaje y a los problemas de mantenibilidad posteriores, mi consejo es que solo se utilice en aquellas zonas críticas que nos generan el cuello de botella de la aplicación. Si queréis un ejemplo lo tenéis aqui. Para aprender ensamblador os hará falta bastante tiempo, pero se puede hacer.

4.MultiThreading: Estamos llegando a un punto un tanto desesperado.Nuestra aplicación pide muchos recursos o nuestros requisitos tal vez están mal dimensionados para la tecnología actual, ya que con un solo procesador no somos capaces de llegar a ellos. Conclusión: Usemos más cores!!! Gracias al uso de hilos (threads) podemos usar más cores en nuestra aplicación haciendo que la velocidad se multiplique por 2, 3 , 4 o más. Problemas? Es complicado encontrar errores y arreglarlos y si nuestro programa no ha seguido los pasos anteriores difícilmente veremos como el rendimiento mejora (uso de memoria compartida demasiado grande, parones del proceso por causas que no nos hemos dado cuenta, etc...) En breves capítulos os explicaré como usar threads.

5. Sockets: Nuestra última esperanza, la granja de cálculo. Ya hemos seguido todos los pasos , tenemos un programa que implementa de forma optima el mejor algoritmo conocido, que aprovecha al 100% el procesador,  tanto a nivel de velocidad como de cores.... pero no llegamos a nuestros requisitos. Nuestra última oportunidad es montar un granja de procesadores (ordenadores conectados en una LAN por ejemplo) y mediante sockets conectar diversas ejecuciones de nuestro programa de forma que el resultado final sea como si tuviéramos un ordenador de muchísimos cores. En el próximo capitulo os explicaré como se puede hacer esto, pero ya os aviso que no os fácil.

6. Otros: Nunca se ha de perder la esperanza, y si no tenemos acceso a un granja de procesadores siempre podemos buscar soluciones imaginativas a nuestros problemas: usando la tarjeta gráfica como procesador matemático o haciendo un uso óptimo de las instrucciones SSE (un tipo especial de ASM paralelo que tienen todos los PC's actuales). 

Si seguís estos pasos vereís como vuestros programas son más rápidos y mantenibles.

Siempre me puedo haber dejado algo, así que si veis algo que falta decidmelo y lo incluiré.

Espero que os haya gustado

Nos vemos!!!


LordPakusBlog

Entradas populares