Optimizar una aplicación Go

En este tutorial, implementarás una aplicación de Go que no es eficiente y que está configurada para recoger datos de perfil. Utiliza la interfaz del creador de perfiles para ver los datos del perfil e identificar posibles optimizaciones. Después, modifica la aplicación, impleméntala y evalúa el efecto de la modificación.

Antes de empezar

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

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

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  4. Enable the required API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

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

    Activa Cloud Shell.

    Al cabo de unos segundos, se abrirá una sesión de Cloud Shell en la consolaGoogle Cloud :

    Sesión de Cloud Shell.

  6. Aplicación de ejemplo

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

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

    El número medio de consultas por segundo que puede gestionar el servidor se determina mediante pruebas de carga del servidor. En cada ronda de pruebas, se llama a un simulador de cliente y se le indica que emita 20 consultas secuenciales. Cuando se completa una ronda, se muestra el número de consultas enviadas por el simulador de cliente, el tiempo transcurrido y el número medio de consultas por segundo.

    El código del 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 1 y el número de rondas 15:

      go run . -version 1 -num_rounds 15
      

      Al cabo de uno o dos minutos, se mostrarán los datos del perfil. Los datos del perfil son similares al siguiente ejemplo:

      Gráfico de llamas inicial del uso del tiempo de CPU.

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

      A continuación, se muestra un ejemplo de la salida 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
      

      En la salida de Cloud Shell se muestra el tiempo transcurrido de cada iteración y la tasa media de solicitudes. Cuando se inicia la aplicación, la entrada "Simulated 20 requests in 17.3s, rate of 1.156069 reqs/sec" indica que el servidor está ejecutando aproximadamente 1 solicitud por segundo. En la última ronda, la entrada "Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs/sec" indica que el servidor ejecuta aproximadamente 1 solicitud cada 5 segundos.

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

    Una forma de maximizar el número de consultas por segundo es identificar los métodos que consumen muchos recursos de CPU y optimizar sus implementaciones. En esta sección, usará perfiles de tiempo de CPU para identificar un método que consuma mucha CPU en el servidor.

    Identificar el uso del tiempo de CPU

    El marco raíz del gráfico de llamas muestra el tiempo total de CPU utilizado por la aplicación durante el intervalo de recogida de 10 segundos:

    Vista ampliada del marco raíz del gráfico tipo llamas.

    En este ejemplo, el servicio utilizado es 2.37 s. Cuando el sistema se ejecuta en un solo núcleo, un uso del tiempo de CPU de 2,37 segundos corresponde a una utilización del 23,7% de ese núcleo. Para obtener más información, consulta Tipos de creación de perfiles disponibles.

    Modificar la aplicación

    Evaluar el cambio

    Para evaluar el cambio, haz lo siguiente:

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

      go run . -version 2 -num_rounds 40
      

      En una sección posterior se muestra que, con la optimización, el tiempo necesario para ejecutar una sola ronda es mucho menor que el de la aplicación sin modificar. Para asegurarnos de que la aplicación se ejecuta el tiempo suficiente para recoger y subir los perfiles, se aumenta el número de rondas.

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

      • Haz clic en AHORA para cargar los datos del perfil más reciente. Para obtener más información, consulta Intervalo de tiempo.
      • En el menú Versión, selecciona 2.

    Por ejemplo, el gráfico de llamas es el siguiente:

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

    En esta figura, el marco raíz muestra el valor 7.8 s. Como resultado de cambiar la función de coincidencia de cadenas, el tiempo de CPU utilizado por la aplicación aumentó de 2,37 segundos a 7,8 segundos, o bien la aplicación pasó de usar el 23,7% de un núcleo de CPU a usar el 78% de un núcleo de CPU.

    La anchura del marco es una medida proporcional del tiempo de uso de la CPU. En este ejemplo, la anchura del marco de GetMatchCount indica que la función usa aproximadamente el 49% del tiempo total de CPU utilizado por la aplicación. En el gráfico de llamas original, este mismo marco ocupaba aproximadamente el 72% de la anchura del gráfico. Para ver el uso exacto del tiempo de CPU, puedes usar la descripción emergente del marco o la lista de funciones de enfoque:

    Lista de funciones de enfoque que muestra el tiempo de uso de la CPU de la versión 2.

    El resultado de Cloud Shell muestra que la versión modificada completa unas 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 tuvo dos efectos diferentes:

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

    • El tiempo de CPU por solicitud, calculado dividiendo el uso de la CPU entre el número de solicitudes por segundo, se redujo del 23,7% al 13,4%.

      Ten en cuenta que el tiempo de CPU por solicitud ha disminuido, aunque el uso del tiempo de CPU ha aumentado de 2,37 segundos, lo que corresponde al 23,7% del uso de un solo núcleo de CPU, a 7,8 segundos, es decir, el 78% de un núcleo de CPU.

    Usar perfiles de montículo asignados para mejorar el uso de recursos

    En esta sección se muestra cómo puedes usar los perfiles de montículo y de montículo asignado para identificar un método que requiera muchas asignaciones en la aplicación:

    • Los perfiles de montículo muestran la cantidad de memoria asignada en el montículo del programa en el momento en que se recoge el perfil.

    • Los perfiles de asignación de memoria muestran la cantidad total de memoria que se asignó en el montículo del programa durante el intervalo en el que se recogió el perfil. Si divides estos valores entre 10 segundos (el intervalo de recogida de perfiles), puedes interpretarlos como tasas de asignación.

    Habilitar la recogida de perfiles de montículo

    1. Ejecuta la aplicación con la versión de la aplicación definida como 3 y habilita la recogida de perfiles de montículo y de montículo asignado.

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

      • Haz clic en AHORA para cargar los datos del perfil más reciente.
      • En el menú Versión, selecciona 3.
      • En el menú Tipo de perfil, selecciona Montículo asignado.

      Por ejemplo, el gráfico de llamas es el siguiente:

      Gráfico tipo llamas de los perfiles de montículo asignado de la versión 3.

    Identificar la tasa de asignación de montículo

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

    Modificar la aplicación

    Evaluar el cambio

    Para evaluar el cambio, haz lo siguiente:

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

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

      • Haz clic en AHORA para cargar los datos del perfil más reciente.
      • En el menú Versión, selecciona 4.
      • En el menú Tipo de perfil, selecciona Montículo asignado.
    3. Para cuantificar el efecto de cambiar readFiles en la tasa de asignación de montículo, compara los perfiles de montículo asignado de la versión 4 con los recogidos en la versión 3:

      Comparación de los perfiles de montículo asignado 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 recogida de perfiles se ha reducido 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 readfiles para el tipo de perfil de montículo asignado.

    4. Para cuantificar el efecto en la recolección de elementos no utilizados, configure una comparación de perfiles de tiempo de CPU. En la siguiente captura de pantalla, se aplica un filtro para mostrar las pilas del recolector de elementos no utilizados de Go runtime.gcBgMarkWorker.*. En la captura de pantalla se muestra que el uso de la CPU para la recogida de elementos no utilizados se ha reducido del 16,8% al 4,97%.

      Comparación del tiempo de uso de la CPU del proceso de recogida de elementos no utilizados en segundo plano de la versión 4 con la versión 3.

    5. Para determinar si el cambio afecta al 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, lo que es considerablemente superior 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 sirve la aplicación puede deberse a que se dedica menos tiempo a la recogida de elementos no utilizados.

    • Para comprender mejor el efecto de la modificación en readFiles, consulta los perfiles de montículo. Una comparación de los perfiles de montículo de la versión 4 con los de la versión 3 muestra que el uso del montículo se ha reducido de 70,95 MiB a 18,47 MiB:

      Comparación del uso del montículo entre la versión 4 y la versión 3.

    Resumen

    En este inicio rápido, se han usado perfiles de tiempo de CPU y de montículo asignado para identificar posibles optimizaciones de una aplicación. Los objetivos eran maximizar el número de solicitudes por segundo y eliminar las asignaciones innecesarias.

    • Se ha identificado una función con un uso intensivo de la CPU mediante perfiles de tiempo de CPU. Tras aplicar un cambio sencillo, la tasa de solicitudes del servidor aumentó de aproximadamente 1 por segundo a 5, 8 por segundo.

    • Al usar los perfiles de montículo asignado, se ha identificado que la shakesapp/server.gofunción readFiles tiene una tasa de asignación alta. Después de optimizar readFiles, la frecuencia de solicitudes del servidor aumentó a 15 solicitudes por segundo y la cantidad media de memoria asignada durante la recogida del perfil 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 artículos: