viernes, 21 de marzo de 2008

Optimización de código

En informática, la optimización de código es el proceso de modificar un sistema para hacer que algún aspecto de su trabajo sea más eficiente o se utilicen menos recursos. Por ejemplo, un programa de computador puede ser optimizado de modo que este se ejecute más rápidamente, o que sea capaz de operar con menos memoria de almacenamiento u otros recursos.

Aunque la palabra optimización comparte la misma raíz que la palabra óptimo, es raro que en un proceso de optimización se obtenga un sistema verdaderamente óptimo. Típicamente, el sistema optimizado solo será óptimo en una aplicación o en un cierto ámbito. Uno podría reducir la cantidad de tiempo que un programa toma para ejecutar cierta tarea pero el precio de hacerlo consume más memoria.

Frecuentemente no hay "un tamaño que se ajusta para todo" esto es, un diseño que trabaje en todos los casos. Así los ingenieros hacen trade-offs para optimizar los atributos de mayor interés. Adicionalmente, el esfuerzo requerido para hacer una pieza de software completamente óptimo - incapaz de alguna mejora adicional - es casi siempre más razonable para los beneficios que serán logrados; así el proceso de optimización podría ser detenido antes que una solución óptima completa haya sido alcanzado. Afortunadamente, es frecuente el caso de que las más grandes mejoras vienen rápido en el proceso.

La optimización puede ocurrir en un número de niveles. En el más alto nivel, el diseño puede ser optimizado para hacer mejor uso de los recursos disponibles. La implementación de este diseño se beneficiará del uso de algoritmos eficientes y la implementación de estos algoritmos beneficiará con la escritura de código de buena calidad.

Trade-off
La optimización generalmente se enfocará en mejorar solo uno o dos aspectos de la performance: tiempo de ejecución, uso de memoria, espacio en disco, ancho de banda, potencia de consumo o algún otro recurso. Esto usualmente requerirá un trade-off: donde un factor es optimizado en expensas de otros. Por ejemplo, incrementando el tamaño del caché mejora la performance en tiempo de ejecución, pero también incrementa el consumo de memoria. Otros comunes trade-offs incluye código claro y conciso.

Cuellos de botella
La optimización requiere determinar un cuello de botella: la parte crítica del código es el consumidor primario de los recursos necesarios. Como una regla de oro, la mejora de un 20% del código es responsable del 80% de los resultados (Principio de Pareto).

El principio de Pareto (también conocido como la regla 80-20) dice que para muchos fenómenos, el 80% de las consecuencias se derivan de un 20% de las causas.

En ciencia del computador, el Principio de Pareto puede ser aplicado para optimización de recursos observando que el 80% de los recursos son típicamente usados por el 20% de las operaciones. En ingeniería de software, es frecuente una mejor aproximación que el 90% del tiempo de ejecución de un programa de computadora es gastado ejecutando el 10% del código (conocida como la ley 90/10 en este contexto).

El diseño de la arquitectura de un sistema abrumadoramente afecta a su rendimiento. La elección del algoritmo afecta la eficiencia más que cualquier otro ítem de diseño. Algoritmos más complejos y estructuras de datos se ejecutan bien con muchos ítems, mientras que algoritmos más simples son más convenientes para pequeñas cantidades de datos.

En algunos casos, añadir más memoria puede ayudar a hacer un programa más rápido.

¿Cuando optimizar?
La optimización puede reducir la legibilidad del código fuente (facilidad con que se puede comprender el propósito, flujo y la operación de una sección de código fuente) que es usado para mejorar la performance. Esto puede complicar programas o sistemas haciéndolos más duros de mantener o debuggear. Como resultado de esto, la optimización o afinamiento de performance es frecuentemente ejecutado al final de la etapa de desarrollo.

Automatización manual y automatizada
La optimización de un sistema completo es usualmente realizado por humanos porque el sistema es demasiado complejo para optimizaciones automáticas. En esta técnica, los programadores o administradores del sistema cambian explícitamente código de modo tal que el sistema mejora. Aunque esto podría conducir a una mejor eficiencia, este es más caro que las optimizaciones automáticas.

Primero que todo, es extremadamente importante usar una herramienta de análisis de performance (profiler) para encontrar la secciones del programa que están tomando la mayor parte de los recursos (el cuello de botella). Los programadores usualmente piensan que ellos tienen una idea clara de donde esta el cuello de botella, pero la intuición falla frecuentemente. Optimizando una pieza insignificante de código típicamente hará poco para ayudar a la performance total.

Cuando el cuello de botella es localizado, la optimización usualmente se inicia rediseñando el algoritmo utilizado en el programa: la mayoría de las veces, un algoritmo particular puede ser adaptado específicamente a un problema particular, produciendo un mejor rendimiento que un algoritmo genérico. Por ejemplo, la tarea de ordenar una enorme lista de ítems se realiza habitualmente con un algoritmo quicksort, que es uno de los algoritmos genéricos más eficientes. Pero si algunos de las características de los ítems son aprovechables (si por ejemplo, ellos ya están ordenados en algún orden particular), un método diferente puede ser usado, o incluso una rutina de ordenamiento hecho a la medida.

Después que se está razonablemente seguro que el mejor algoritmo es seleccionado, la optimización de código puede comenzar: bucles pueden ser desenrollados (para disminuir la sobrecarga del bucle, aunque a menudo esto puede conducir a disminuir la velocidad si esto sobrecarga el caché del CPU), tipos de dato tan pequeños como sea posible usar, aritmética de enteros puede ser usado en lugar de punto-flotante, y así sucesivamente.

Los cuellos de botella de performance pueden ser debidos a limitaciones del lenguaje de programción antes que algoritmos o estructuras de datos usadas en el programa. A veces, una parte crítica del programa puede ser re-escrita en un lenguaje diferente que da acceso más directo a la máquina subyacente. Por ejemplo, es común para lenguajes de muy alto nivel como Pitón tener módulos escritos en C para mayor velocidad. Programas ya escritos en C pueden tener módulos escritos en ensamblador.

La reescritura vale la pena debido a una regla general conocida como la ley 90/10, que dice que el 90% del tiempo es gastado en el 10% del código, y solo el 10% del tiempo en el restante 90% del código. Así poniendo esfuerzo intelectual en optimizar solo una parte pequeña del programa puede tener un enorme efecto en la velocidad total si la parte(s) correcta(s) puede(n) ser localizada(s).

Tiempo tomado para la optimización
En algunos casos, el tiempo tomado para la optimización en si mismo puede ser un problema.

En un proyecto de software, la optimización de código usualmente no añade un nuevas características, y peor aún, esto podría romper ciertas funcionalidades. Debido a que código optimizado tiene menos legibilidad que un código sencillo, la optimización puede bien afectar la legibilidad del programa. En resumen, la optimización se convierte en un costo y es importante estar seguro que la inversión vale la pena.

1 comentario:

William Ccente dijo...

Actualmente tambien se implanto en Caja CREDINKA el 2007 con 6meses aproximados de migracion y estamos en un proceso de refinamiento de nuestros procesos.