Tutorial: Optimizar aplicaciones de Go

En este tutorial, desplegarás una aplicación Go de forma ineficiente que está configurada para recoger datos de perfil. Utiliza la interfaz de Profiler para consultar los datos de perfil e identificar posibles optimizaciones. A continuación, modificas la aplicación, la despliegas y evalúas el efecto de la modificación.

Antes de empezar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  4. Para habilitar la API de Cloud Profiler en tu proyecto, en el panel de navegación de la consola de Google Cloud, haz clic en Perfil o usa el siguiente botón:

    Ir a Profiler

  5. Para abrir Cloud Shell, en la barra de herramientas de la consola de Google Cloud, haz clic en Activar Cloud Shell:

    Activa Cloud Shell.

    Después de unos segundos, se abrirá una sesión de Cloud Shell en la consola de Google Cloud:

    Sesión de Cloud Shell.

Aplicación de ejemplo

El objetivo principal es maximizar el número de consultas por segundo que puede procesar el servidor. Un objetivo secundario es reducir el uso de memoria al eliminar las asignaciones de memoria innecesarias.

El servidor utiliza un framework gRPC, recibe una palabra o frase y, a continuación, devuelve el número de veces que la palabra o la frase aparecen en las obras de Shakespeare.

El número medio de consultas por segundo que el servidor puede gestionar se determina mediante pruebas de carga. En cada ronda de pruebas, se llama a un emulador del cliente y se le pide que emita 20 consultas secuenciales. Al finalizar una ronda, se muestra el número de consultas que ha enviado el simulador del cliente, el tiempo transcurrido y el promedio de consultas por segundo.

El código de servidor es ineficiente a propósito.

Ejecutar la aplicación de ejemplo

Descarga y ejecuta la aplicación de ejemplo:

  1. En Cloud Shell, ejecuta los siguientes comandos:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    cd golang-samples/profiler/shakesapp
    
  2. Ejecuta la aplicación con la versión configurada como 1 y el número de vueltas a 15:

    go run . -version 1 -num_rounds 15
    

    Después de uno o dos minutos, se mostrarán los datos del perfil. Los datos del perfil se parecen a los del siguiente ejemplo:

    Gráfico inicial inicial para el uso de tiempo de CPU.

    En la captura de pantalla, observa que el tipo de perfil se ha establecido en CPU time. Esto indica que los datos de uso de la CPU se muestran en el gráfico tipo llamas.

    A continuación se muestra un ejemplo de una impresión impresa en Cloud Shell:

    $ go run . -version 1 -num_rounds 15
    2020/08/27 17:27:34 Simulating client requests, round 1
    2020/08/27 17:27:34 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 17:27:34 profiler has started
    2020/08/27 17:27:34 creating a new profile via profiler service
    2020/08/27 17:27:51 Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec
    2020/08/27 17:27:51 Simulating client requests, round 2
    2020/08/27 17:28:10 Simulated 20 requests in 19.02s, rate of 1.051525 reqs / sec
    2020/08/27 17:28:10 Simulating client requests, round 3
    2020/08/27 17:28:29 Simulated 20 requests in 18.71s, rate of 1.068947 reqs / sec
    ...
    2020/08/27 17:44:32 Simulating client requests, round 14
    2020/08/27 17:46:04 Simulated 20 requests in 1m32.23s, rate of 0.216849 reqs / sec
    2020/08/27 17:46:04 Simulating client requests, round 15
    2020/08/27 17:47:52 Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec
    

    El resultado de Cloud Shell muestra el tiempo transcurrido para cada iteración y el porcentaje medio de solicitudes. Cuando se inicia la aplicación, la entrada "Simulación de 20 solicitudes en 17,3, tasa de 1,156069 solicitudes por segundo" indica que el servidor está ejecutando una solicitud por segundo. En la última ronda, la entrada "Simulación de 20 solicitudes en 1 m48,03 s, una tasa de 0,185134 solicitudes por segundo" indica que el servidor ejecuta una solicitud cada 5 segundos.

Usar perfiles de tiempo de CPU para maximizar las consultas por segundo

Un método para maximizar el número de consultas por segundo es identificar los métodos intensivos de CPU y optimizar sus implementaciones. En esta sección, utilizarás perfiles de tiempo de CPU para identificar un método que requiera un uso intensivo de la CPU en el servidor.

Identificar el uso de tiempo de CPU

El marco raíz del gráfico de llama muestra el tiempo total de CPU que ha utilizado la aplicación durante el intervalo de recopilación de 10 segundos:

Vista ampliada del marco raíz del gráfico de flamenco.

En este ejemplo, el servicio ha usado 2.37 s. Cuando el sistema se ejecuta en un solo núcleo, el uso del tiempo de CPU de 2,37 segundos se corresponde con el uso del 23,7% de ese núcleo. Para obtener más información, consulta la sección sobre tipos de perfiles disponibles.

Modificar la aplicación

Evaluar el cambio

Para evaluar el cambio, sigue estos pasos:

  1. Ejecuta la aplicación con la versión de la aplicación configurada como 2:

    go run . -version 2 -num_rounds 40
    

    Una sección posterior muestra que, con la optimización, el tiempo que se tarda en ejecutar una sola ronda es mucho menor que el de la aplicación sin modificar. Si quieres que la aplicación se ejecute el tiempo suficiente para recoger y subir perfiles, aumenta el número de rondas.

  2. Espera a que se complete la aplicación y consulta los datos de perfil de esta versión:

    • Haga clic en AHORA para cargar los datos del perfil más recientes. Para obtener más información, consulta la sección Periodo.
    • En el menú Versión, seleccione 2.

Por ejemplo, el gráfico de llamas se muestra de la siguiente manera:

Gráfico plano que muestra el uso de tiempo de CPU de la versión 2.

En esta cifra, el marco raíz muestra un valor de 7.8 s. Al cambiar la función de concordancia de cadena, el tiempo de CPU que ha utilizado la aplicación aumentó de 2,37 segundos a 7,8 segundos. La aplicación pasó del 23,7 % de un núcleo de CPU a usar 78. Porcentaje de un núcleo de CPU

El ancho del marco es una medida proporcional del uso del tiempo de CPU. En este ejemplo, el ancho del marco de GetMatchCount indica que la función utiliza aproximadamente un 49% de todo el tiempo de CPU que utiliza la aplicación. En el gráfico de llamas original, el mismo fotograma era aproximadamente un 72% del ancho. Si quieres ver el uso exacto del tiempo de CPU, puedes usar la descripción emergente correspondiente o usar la lista de funciones de Focus:

Seleccionar la lista de funciones en la que se muestra el uso de tiempo de CPU de la versión 2.

El resultado en Cloud Shell muestra que la versión modificada está completando 5,8 solicitudes por segundo:

$ go run . -version 2 -num_rounds 40
2020/08/27 18:21:40 Simulating client requests, round 1
2020/08/27 18:21:40 Stackdriver Profiler Go Agent version: 20200618
2020/08/27 18:21:40 profiler has started
2020/08/27 18:21:40 creating a new profile via profiler service
2020/08/27 18:21:44 Simulated 20 requests in 3.67s, rate of 5.449591 reqs / sec
2020/08/27 18:21:44 Simulating client requests, round 2
2020/08/27 18:21:47 Simulated 20 requests in 3.72s, rate of 5.376344 reqs / sec
2020/08/27 18:21:47 Simulating client requests, round 3
2020/08/27 18:21:51 Simulated 20 requests in 3.58s, rate of 5.586592 reqs / sec
...
2020/08/27 18:23:51 Simulating client requests, round 39
2020/08/27 18:23:54 Simulated 20 requests in 3.46s, rate of 5.780347 reqs / sec
2020/08/27 18:23:54 Simulating client requests, round 40
2020/08/27 18:23:58 Simulated 20 requests in 3.4s, rate of 5.882353 reqs / sec

El pequeño cambio en la aplicación ha tenido dos efectos diferentes:

  • El número de solicitudes por segundo aumentó de menos de 1 por segundo a 5,8 por segundo.

  • El tiempo de CPU por solicitud, que se calcula dividiendo el uso de CPU por el número de solicitudes por segundo, disminuye a un 13,4% del 23,7%.

    El tiempo de CPU por solicitud se ha reducido a pesar de que el uso de tiempo de CPU aumentó de 2,37 segundos, lo que corresponde al uso del 23,7% de un núcleo de CPU único, a 7,8 segundos o al 78% de un núcleo de CPU.

Usar perfiles de perfiles asignados para mejorar el uso de recursos

En esta sección se muestra cómo usar el perfil de la pila y de la pila asignada para identificar un método de asignación de asignación en la aplicación:

  • Los perfiles de japón muestran la cantidad de memoria asignada en la pila del programa en el momento en que se recopila el perfil.

  • Los perfiles de pila asignados muestran la cantidad total de memoria asignada en la pila del programa durante el intervalo en el que se recogió el perfil. Si divide estos valores entre 10 segundos y el intervalo de recogida de perfiles, podrá interpretarlos como tarifas de asignación.

Habilitar la recogida de perfiles de pila

  1. Ejecuta la aplicación con la versión de la aplicación configurada como 3 y habilita la colección de perfiles de pila y de pila asignados.

    go run . -version 3 -num_rounds 40 -heap -heap_alloc
    
  2. Espera a que se complete la aplicación y consulta los datos de perfil de esta versión:

    • Haga clic en AHORA para cargar los datos del perfil más recientes.
    • En el menú Versión, seleccione 3.
    • En el menú Tipo de perfil, selecciona Volar asignado.

    Por ejemplo, el gráfico de llamas se muestra de la siguiente manera:

    Gráfico plano de perfiles de pila asignados para la versión 3.

Identificar la tarifa de asignación de apilaciones

El marco raíz muestra la cantidad total de pila que se asignaron durante los 10 segundos en los que se recogió un perfil, promediado de todos los perfiles. En este ejemplo, el marco raíz muestra que, de media, 1,535 GiB de memoria se ha asignado.

Modificar la aplicación

Evaluar el cambio

Para evaluar el cambio, sigue estos pasos:

  1. Ejecuta la aplicación con la versión de la aplicación configurada como 4:

    go run . -version 4 -num_rounds 60 -heap -heap_alloc
    
  2. Espera a que se complete la aplicación y consulta los datos de perfil de esta versión:

    • Haga clic en AHORA para cargar los datos del perfil más recientes.
    • En el menú Versión, seleccione 4.
    • En el menú Tipo de perfil, selecciona Perfil asignado.
  3. Para calcular el efecto de cambiar readFiles en la tasa de asignación de montículo, compara los perfiles de pila asignados de la versión 4 con los recopilados para tres:

    Comparación de los perfiles de pila asignados entre las versiones 4 y 3.

    La descripción emergente del marco raíz muestra que, con la versión 4, la cantidad media de memoria asignada durante la recopilación de perfiles se redujo en 1.301 GiB, en comparación con la versión 3. La descripción emergente de readFiles.func1 muestra una disminución de 1,045 GiB:

    Comparación de la descripción emergente de los archivos de lectura del tipo de perfil de pila asignado.

  4. Si quieres cuantificar el efecto sobre la recolección de memoria residual, configura una comparación de perfiles de tiempo de CPU. En la siguiente captura de pantalla se aplica un filtro para mostrar las pilas del recopilador de basura Go.runtime.gcBgMarkWorker.* En la captura de pantalla se muestra que el uso de la CPU para la recolección de memoria residual se reduce hasta un 46,6% del 16,8%.

    Comparación del uso del tiempo de CPU del proceso de recolección de memoria residual en segundo plano de la versión 4 a la 3.

  5. Para determinar si hay un impacto en el número de solicitudes por segundo que gestiona la aplicación, consulta el resultado en Cloud Shell. En este ejemplo, la versión 4 completa hasta 15 solicitudes por segundo, que son sustancialmente superiores a las 5,8 solicitudes por segundo de la versión 3:

    $ go run . -version 4 -num_rounds 60 -heap -heap_alloc
    2020/08/27 21:51:42 Simulating client requests, round 1
    2020/08/27 21:51:42 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 21:51:42 profiler has started
    2020/08/27 21:51:42 creating a new profile via profiler service
    2020/08/27 21:51:44 Simulated 20 requests in 1.47s, rate of 13.605442 reqs / sec
    2020/08/27 21:51:44 Simulating client requests, round 2
    2020/08/27 21:51:45 Simulated 20 requests in 1.3s, rate of 15.384615 reqs / sec
    2020/08/27 21:51:45 Simulating client requests, round 3
    2020/08/27 21:51:46 Simulated 20 requests in 1.31s, rate of 15.267176 reqs / sec
    ...
    

    El aumento de las consultas por segundo que publica la aplicación puede deberse a que menos tiempo se dedique a la recolección de memoria residual.

  • Para obtener un conocimiento más profundo del efecto de la modificación a readFiles, consulta los perfiles de montículo. Si se comparan los perfiles de los perfiles de la versión 4 con los de la versión 3, el uso de la pila disminuye a 18,47 MiB de 70,95 MiB de 70,95 MiB:

    Comparación del uso de la versión 4 en la versión 3.

Resumen

En este inicio rápido, se usaron el tiempo de CPU y los perfiles de pila asignados para identificar posibles optimizaciones en una aplicación. Los objetivos era maximizar el número de solicitudes por segundo y eliminar las asignaciones innecesarias.

  • Si se usan perfiles de tiempo de CPU, se ha identificado una función que consume mucha CPU. Después de aplicar un cambio sencillo, el porcentaje de solicitudes del servidor aumentó a 5,8 por segundo, aproximadamente de 1 por segundo.

  • Al utilizar perfiles de pila asignados, se ha identificado que la función shakesapp/server.go de readFiles tiene una tasa de asignación alta. Después de optimizar readFiles, el porcentaje de solicitudes del servidor aumentó a 15 solicitudes por segundo y la cantidad media de memoria asignada durante la recogida de perfiles de 10 segundos disminuyó en 1.301 GiB.

Siguientes pasos

Para obtener información sobre cómo ejecutar el agente de Cloud Profiler, consulta los siguientes enlaces: