# Perfilación

La recopilación de información sobre el comportamiento de un programa, específicamente su uso de recursos, se denomina perfilación (del término en inglés: profiling). El recurso más importante en términos de computación de alto rendimiento es el tiempo de ejecución.
Una estrategia común de creación de perfiles es averiguar cuánto tiempo se le dedica a las diferentes funciones, y tal vez incluso a las líneas, de un código para identificar los puntos calientes, es decir, las partes del programa que requieren la fracción dominante de tiempo de ejecución. Estos puntos calientes se analizan posteriormente en busca de posibles oportunidades de optimización.
En todos los procesadores actuales se permite conocer, en profundidad, el uso de recursos dentro del chip y el sistema; de hecho, existen circunstancias en las que ya no se puede hacer nada para acelerar más un código de serie y es fundamental que, el usuario, pueda identificar el punto en el que los esfuerzos de optimización adicionales no son útiles.

## Perfilado en tiempo de ejecución basado en funciones y líneas

En general, se utilizan dos tecnologías para la creación de perfiles basados en funciones y líneas:
* Instrumentación de código: La instrumentación funciona permitiendo que el compilador modifique cada llamada de función, insertando algún código que registre la llamada, su llamador (o la pila de llamadas completa) y probablemente cuanto tiempo requirió. Esta técnica en curso conlleva una sobrecarga significativa, especialmente si el código contiene muchas funciones con un tiempo de ejecución corto. El código de instrumentación intentará compensar eso, pero siempre hay cierta incertidumbre residual.
* Muestreo de código: El muestreo es menos invasivo, pues el programa se interrumpe a intervalos periódicos, por ejemplo, 10 milisegundos, y se registra el contador del programa (y posiblemente la pila de llamadas actual). Necesariamente, este proceso es estadístico por naturaleza, pero cuanto más se ejecute el código, más precisos serán los resultados.
Si el compilador ha equipado el código objeto con la información adecuada, el muestreo puede entregar información sobre el tiempo de ejecución hasta la línea fuente e incluso el nivel del código de la máquina.
La instrumentación está necesariamente limitada a funciones o bloques básicos (código con una entrada y un punto de salida sin llamadas ni saltos intermedios) por razones de eficiencia.

### Perfilado de funciones

El perfil plano contiene información sobre los tiempos de ejecución de todas las funciones del programa y la frecuencia con la que se llamaron. Por cada función hay una fila con los datos correspondientes.
Estos datos pueden ser:
* Porcentaje de tiempo: Porcentaje de todo el tiempo de ejecución usado exclusivamente para esa función, es decir, no toma en cuenta ninguna de sus llamadas.
* Segundos acumulados: Suma acumulativa de tiempos de ejecución exclusivos de todas las funciones (inclusive).
* Segundos de la función: Número de segundos utilizados por esta función (exclusivo). De forma predeterminada, la lista está ordenada según este campo.
* Llamadas: Veces que la función fue llamada.
* Milisegundos por llamada de la función: Cantidad promedio de los milisegundos por llamada (exclusivo).
* Milisegundos por llamada totales. Cantidad promedio de los milisegundos por llamada que se gastaron por la función, incluyendo sus llamadas (inclusivo).
Aunque el perfil plano contiene bastante información, no dice cómo la contribución del tiempo de ejecución de una cierta función está compuesta por varios llamadores diferentes, qué otras funciones (llamadas) se llaman desde él ni qué contribución al tiempo de ejecución incurren a su vez. Estos datos son proporcionados por el gráfico de mariposa o el perfil de gráfico de llamadas.
Cada sección del gráfico de llamadas pertenece exactamente a una función, que se enumera junto con un índice en ejecución (extremo izquierdo). Las funciones enumeradas arriba de esta línea son las personas que llaman a la función actual, mientras que las que se enumeran a continuación son sus destinatarios. Las llamadas recursivas se contabilizan.
Los campos son:
* Porcentaje de tiempo: El porcentaje de todo el tiempo de ejecución gastado en una función, incluido sus llamadas (tiempo inclusivo). Esto debe ser idéntico al producto del número de llamadas con el tiempo por llamada en el perfil plano.
* La función misma: Para cada función indexada, es el tiempo de ejecución exclusivo (idéntico al del perfil plano). Para sus llamadas, denota el tiempo inclusivo esta función (cada calle) contribuido a cada llamador (esta función).
* Hijos: Para cada función indexada, esto es inclusivo menos el tiempo de ejecución exclusivo, es decir, la contribución de todos sus destinatarios al tiempo inclusivo. Parte de este tiempo contribuye al tiempo de ejecución inclusivo de cada uno de los llamadores de la función y se indica en las respectivas filas de llamadores. Las filas de los destinatarios de esta columna designan la contribución de los destinatarios de cada destinatario al tiempo de ejecución inclusivo de la función.
* Llamado: Indica el número de veces que la función fue llamada (probablemente dividida en contribuciones recursivas más contribuciones no recursivas. La fracción de la cantidad de llamadas provenientes de cada llamador que llama se muestra en la fila de la función que llama, mientras que la fracción de llamadas para cada llamada que se inició desde esta función se puede encontrar en las filas de la función que llama.
Existen herramientas que pueden representar el perfil de la mariposa de forma gráfica, lo que permite navegar por el árbol de llamadas y encontrar rápidamente la ”ruta crítica”, es decir, la secuencia de funciones (desde la raíz hasta alguna hoja) que muestra las contribuciones inclusivas dominantes por todos sus elementos.

### Perfilado basado en líneas

Si la zona activa en tales funciones no se puede encontrar, se deben utilizar herramientas para la creación de perfiles basados en líneas.
Con un perfilado basado en líneas, el usuario puede extraer información sobre un binario específico. Entre otras cosas, puede ser una lista de fuentes anotada en la que cada línea de fuente, acompañada por el número de resultados de muestreo (primera columna) y el porcentaje relativo del total de muestras de programas (segunda columna)

### Perfilación Automática.

El perfilado automático hace uso de software especializado y herramientas nativas del sistema operativo, o compiladores, para recolectar datos sobre el rendimiento de un programa de forma automatizada; por lo que no es necesario hacer modificaciones manuales en el código para obtener las métricas usando estos métodos. Algunos de los métodos para los que son usados son:

### Perfilación Manual.

La recolección manual de datos implica la inclusión de instrucciones en el código que se encarguen de recolectar los datos en el código que nos permitan conocer su eficiencia. Esto puede hacerse usando funciones nativas del lenguaje, mediante el uso de bibliotecas especializadas o consultas a APIs. Este método puede resultar más efectivo cuando ya se ha identificado previamente un hotspot y ahora se busca extraer información más puntual sobre su comportamiento para identificar las áreas donde puede ser optimizado. Algunos métodos de perfilación manual son: 1) Marcadores de tiempo Se colocan marcadores de tiempo al inicio y final de una sección de código, o llamada a función, para obtener el total de tiempo que toma su ejecución. Si dentro de esta función se hace uso de otras, esto afectará el tiempo de la misma; por lo que la interdependencia de funciones no optimizadas puede suponer un obstáculo para la medición efectiva. 2) Contadores A diferencia de los marcadores de tiempo, un contador es utilizado para registrar cuántas veces es llamada una función dentro de la ejecución de un programa, conocer esto puede ser un primer paso cuando se comienza a buscar hotspots; una función que es llamada constantemente puede ocasionar cuellos de botella si no se encuentra bien optimizada. En casos donde la sustitución del cuerpo de la función por su llamado es posible, el tiempo de ejecución puede ser disminuido. 3) Registro de eventos Guarda información sobre la ocurrencia de acciones específicas dentro de la ejecución de un programa, como la modificación de una variable o la llamada a una función. Conocer el contexto de la ocurrencia de un cambio puede ser de ayuda al determinar cómo este puede ser mejorado; como en las estructuras if-else, donde la opción de mayor uso debería colocarse al inicio de la condicional, para evitar la mayor cantidad de comparaciones innecesarias.
* Profiling profilers: Se ejecutan a la par del programa y almacenan información referente a la ejecución.
* Análisis de memoria: Se trata de sistemas que monitorean el uso de la memoria con el fin de detectar comportamientos inusuales derivados de un mal manejo de la misma.
* Análisis de rendimiento web: Especialmente útil en aplicaciones cuya funcionalidad depende de la red; pueden registrar los tiempos de carga de las páginas, así como el tiempo de resolución de las solicitudes.

## Metodologías de perfilado

Las metodologías de perfilado se refieren a enfoques sistemáticos y técnicas utilizadas para analizar y mejorar el rendimiento de un software. Estas metodologías están diseñadas para identificar áreas críticas en el código que pueden estar causando cuellos de botella o ralentizando la ejecución del programa. Al aplicar metodologías de perfilado, los desarrolladores pueden obtener información detallada sobre el tiempo de ejecución, el uso de recursos y otros aspectos relevantes del rendimiento del software, lo que les permite realizar ajustes y optimizaciones para mejorar su eficiencia y velocidad. La primera fase en el perfilado de rendimiento se centra en identificar los puntos críticos en términos de tiempo de ejecución, es decir, los ciclos de reloj. Sin embargo, cuando se trata de identificar la razón exacta por la cual un código es lento, o si simplemente se desea saber por qué recurso está limitado, los ciclos de reloj resultan insuficientes. Afortunadamente, los procesadores hoy en día cuentan con un pequeño número de contadores de rendimiento, que son registros especiales en el chip que se incrementan cada vez que ocurre un evento específico. Entre los cientos de eventos que generalmente pueden ser monitoreados, existen algunos que son especialmente útiles para el perfilado:
* Number of bus transactions (Número de transacciones de bus)
Esta es una metodología de perfilado que se enfoca en el número de transacciones de bus, es decir, transferencias de líneas de caché. Este enfoque implica monitorear la cantidad de veces que los datos se transfieren entre la memoria principal y la caché del procesador a través del bus del sistema. Las transferencias de líneas de caché son indicativas de la eficiencia del uso de la memoria caché por parte del programa. Los eventos como "fallos de caché" son comúnmente utilizados en lugar de las transacciones de bus, ya que proporcionan información más específica sobre el rendimiento de la memoria caché. Un fallo de caché ocurre cuando el procesador necesita acceder a datos que no están almacenados en la caché y, por lo tanto, debe recuperarlos desde la memoria principal. Estos fallos pueden tener un impacto significativo en el rendimiento del programa, ya que implican un mayor tiempo de acceso a los datos.
* Number of loads and store (Número de cargas y almacenamientos)
Esta metodología de perfilado se centra en monitorear la cantidad de veces que se realizan operaciones de carga (loads) y almacenamiento (stores) en un programa. Estas operaciones son indicativas del acceso a datos en la memoria, ya sea para leer información (cargas) o para escribir información (almacenamientos). Al observar el número de cargas y almacenamientos, los desarrolladores pueden obtener información sobre los patrones de acceso a datos en el programa y detectar posibles áreas de ineficiencia. Por ejemplo, un alto número de cargas puede indicar que el programa está accediendo frecuentemente a datos en la memoria, lo que puede ralentizar la ejecución debido a los tiempos de acceso a la memoria. Del mismo modo, un alto número de almacenamientos puede sugerir que el programa está realizando muchas operaciones de escritura en la memoria, lo que también puede afectar negativamente el rendimiento. En conjunto con las transacciones de bus, esto puede proporcionar una indicación de qué tan eficientemente se utilizan las líneas de caché para la computación.
* Number of floating-point operations (Número de operaciones de punto flotante)
Se centra en cuantificar y analizar las operaciones que involucran números de punto flotante dentro de un programa. Este enfoque es especialmente útil en aplicaciones que realizan cálculos intensivos, como simulaciones científicas, procesamiento de imágenes o análisis de datos. Permite entender la carga computacional del programa y puede revelar algunas oportunidades para intentar mejorar la eficiencia del algoritmo, como por ejemplo reducir el número de operaciones redundantes. Sin embargo, es importante destacar que esta métrica es a menudo sobrevalorada, pues aunque el número de operaciones de punto flotante por ciclo de reloj esté cerca del máximo teórico (ya sea dado por el rendimiento pico de la CPU), es poco probable que la optimización realizada, dadas los resultados de esta métrica, logre mejoras significativas, y probablemente sean necesarios cambios algorítmicos. No es recomendable depender exclusivamente de esta métrica al evaluar el rendimiento y es necesario considerar otros factores, como la transferencia de datos, para obtener un análisis completo y preciso del comportamiento del programa.

## Herramientas de perfilado

Son herramientas de software que se utilizan para analizar y medir el rendimiento de una aplicación o sistema informático. Estas herramientas permiten a los desarrolladores identificar cuellos de botella en el código y mejorar el rendimiento de una aplicación. Algunas herramientas populares de perfilado incluyen: Profiler de CPU: Herramienta que mide el tiempo que tarda una aplicación en realizar una determinada tarea y el uso de la CPU durante ese tiempo. Profiler de memoria: Herramienta que mide el uso de la memoria de una aplicación y detecta fugas de memoria. Profiler de E/S: Herramienta que mide el tiempo que tarda una aplicación en realizar operaciones de entrada y salida. Profiler de red: Herramienta que mide el tiempo que tarda una aplicación en enviar y recibir datos a través de una red. Profiler de rendimiento web: Herramienta que mide el tiempo de carga de una página web y detecta cuellos de botella en el código. A continuación se listan algunas herramientas de perfilado, de acuerdo al lenguaje de programación que se utilice:
* Java
1) Jprofiler Es la herramienta más popular usada en aplicaciones estándar y empresariales ya que cuenta con una interfaz de usuario muy intuitiva, las características principales son el perfilamiento de hilos, encontrar fugas de memoria y resolver cuellos de botella. Es compatible con plataformas Windows, MacOs, Linux, FreeBSD, Solaris, AIX y HP-UX. 2) YourKit Esta herramienta se utiliza para analizar el rendimiento de aplicaciones java en desarrollo y producción, cuenta con funciones básicas para visualizar hilos, recolecciones de basura, uso de memoria y fugas de memoria, con soporte para perfiles locales y remotos. También es útil para la creación de perfiles de CPU, que permiten la creación de perfiles enfocados en ciertas áreas de nuestro código, análisis de E/S y de multithreading. Es compatible con plataformas Windows, MacOs, lINUX, etc.
* Lenguaje C
1) Gproof Es una herramienta de análisis de rendimiento que produce un perfil de ejecución de programas en C, Pascal o FORTRAN77, Gprof calcula la cantidad de tiempo empleado en cada rutina. Después, estos tiempos se propagan a lo largo de los vértices del grafo de llamadas. 2) Intel VTune Profiler Es una herramienta de análisis de rendimiento para plataformas x86 y x64 que proporciona una amplia variedad de información sobre el rendimiento del sistema, es de libre uso y con perfilamiento de memoria, CPU y análisis de almacenamiento en procesadores Intel.
* Python
1) cProfile Viene incluido en la biblioteca estándar de python, éste rastrea el llamado de las funciones dentro del programa y genera una lista con las funciones que más tiempo tardan. Tiene tres puntos fuertes que son:
1. Que se incluye con la biblioteca estándar.
2. Que perfila una serie de estadísticas diferentes sobre el comportamiento de las llamadas, esto permite determinar si una función es lenta por sí misma o si llama a otras funciones que son lentas.
3. Que puede restringir cProfile libremente, es decir, mostrar toda la ejecución de un programa o activar la creación de perfiles solo cuando se ejecuta la función seleccionada.
2) FunctionTrace Simular a la herramienta anterior genera reportes de las funciones llamadas y la memoria usada por el programa en ejecución guardándolo en un JSON con posibilidad de usar el Firefox Profiler.