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:Herramientas de perfilado de bajo nivel. (03 junio, 2021). IBM Knowledge Center. https://www.ibm.com/docs/es/rdfa-and-l/9.1.0?topic=reference-low-level-data-profiling-tools
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)