domingo, 13 de diciembre de 2015

Acelerando las Construcciones de Gradle usando Gradle Daemon

Introducción

Los proyectos grandes pueden tomar bastante tiempo para compilarse y construir los paquetes finales (JAR, WAR, EAR, etc.), dada la gran cantidad de líneas de código, módulos y de dependencias que puedan tener.

Gradle mediante la Construcción Incremental ha ayudado a disminuir dichos tiempos al ejecutar cada tarea del proceso de construcción sólo cuando detecta cambios en los datos de entrada de dichas tareas (archivos, parámetros, etc.), sin embargo ahora se cuenta con una herramienta dentro de Gradle que ayuda a disminuir aún más dichos tiempos: Gradle Daemon.

Básicamente se trata de un proceso demonio (daemon) que luego de la primera construcción de un proyecto Gradle permanece en el sistema esperando la próxima construcción para así no tener que volver a cargar en memoria todos los componentes de Gradle necesarios, todo esto sin que se requiera de mayor configuración o gestión del proceso por parte del desarrollador.

El proceso permanece en espera por aproximadamente 3 horas, luego de las cuales si no se ha realizado ninguna construcción con Gradle se terminará automáticamente. También vale la pena aclarar que si la configuración del proyecto (cambio de versión de Java, codificación del texto, etc.) el proceso que hizo la construcción anterior a dichos cambios ya no se considerará compatible y Gradle automáticamente iniciará un nuevo proceso demonio.

Habilitando Gradle Daemon

Cuando se está usando un IDE con integración con Gradle ya se tiene habilitado por defecto el uso de Gradle Daemon, sin embargo para las construcciones realizadas desde línea de comandos (terminal) existen 4 maneras de habilitarlo:
  • Agregar el siguiente valor a la variable de entorno del sistema operativo "GRADLE_OPTS": -Dorg.gradle.daemon=true
  • Agregar el siguiente valor al archivo "<GRADLE_USER_HOME>/gradle.properties" (GRADLE_USER_HOME normalmente es "<carpeta_usuario>/.gradle/", por ejemplo "/home/usuario/.gradle/" o "C:\Users\usuario\.gradle\"): org.gradle.daemon=true
  • A nivel sólo del proyecto, agregar lo siguiente en el archivo "gradle.properties": org.gradle.daemon=true
  • A nivel sólo de la construcción específica, agregando el siguiente parámetro en el comando: --daemon


Deshabilitando Gradle Daemon

Para las construcciones iniciadas usando comandos Gradle Daemon no se encuentra habilitado, así que si no se ha indicado explícitamente que se quiere usar dicho proceso, con alguno de los métodos mencionados en el punto anterior, no se requiere ninguna acción.

En caso de tener alguna de las anteriores configuraciones o estar usando un IDE, puede indicarse el valor "false" en alguna de las 3 primeras configuraciones o indicar en el comando el parámetro "--no-daemon", el cual tiene precedencia sobre los demás.

Un ejemplo de cuando puede ser necesario deshabilitar este proceso es cuando se usa un servidor de integración continua, ya que en primer lugar no debería ser tan frecuente la construcción del proyecto (o por lo menos no tanto como en un ambiente de desarrollo) y también es un caso en el que se quiere que la construcción se realice de manera aislada, sin depender de ejecuciones previas para obtener resultados que sólo dependan de los cambios actuales en el proyecto, bien sea resultado de ejecución de pruebas o generación del archivo compilado con el proyecto.

Usando Gradle Daemon

Para mantener el ejemplo simple se usará como base el API REST desarrollado en un post anterior.

Como se tiene inicialmente, sin Gradle Daemon se obtiene el siguiente resultado:

gradlew.bat build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 6.924 secs

Luego al agregar "org.gradle.daemon=true" al archivo "gradle.properties" y borrar el contenido de la carpeta "build" para que realice la construcción de nuevo:

gradlew.bat build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 3.146 secs

Puede verse que se tiene una diferencia casi del 50% en el tiempo. Es posible que las primeras construcciones usando Gradle Daemon no tengan una diferencia en tiempo tan grande, pero el equipo de Gradle ha documentado que generalmente después de la quinta o décima vez la diferencia empieza a notarse más, debido a que la optimización de código que hace la máquina virtual es progresiva, no simultánea.

Más Información

Mayores detalles acerca de Gradle Daemon pueden encontrarse en la documentación oficial: https://docs.gradle.org/current/userguide/gradle_daemon.html

Existe también una funcionalidad adicional de Gradle que permite mejorar los tiempos de construcción: Construcción en Paralelo. Esta opción permite que los proyectos multi-módulo puedan construir sus módulos simultáneamente, en lugar de secuencialmente, sin embargo sólo debe usarse en los casos en que los módulos sean independientes entre sí (Decoupled Projects). Más información puede encontrarse  también en la documentación oficial de Gradle: https://docs.gradle.org/current/userguide/multi_project_builds.html#sec:parallel_execution

También pueden encontrarse otros posts acerca de Gradle en: http://nombre-temp.blogspot.com/2016/01/tutorial-gradle.html

4 comentarios:

  1. Habilitando Gradle Daemon con la opcion Agregar el siguiente valor a la variable de entorno del sistema operativo "GRADLE_OPTS": -Dorg.gradle.daemon=true
    me sale este error
    Error: no se ha encontrado o cargado la clase principal Dorg.gradle.daemon=true
    como puedo solucionar su ayuda porfavor

    ResponderBorrar
    Respuestas
    1. Al usar el archivo "gradle.properties" en el proyecto o el parámetro "--daemon" pasa lo mismo?
      Es posible que tenga instalada una versión desactualizada de Gradle.

      Borrar
    2. esto es lo que me sale :
      C:\xampp\htdocs\ContactosGADC>ionic platform add android
      Adding android project...

      Creating Cordova project for the Android platform:

      Path: platforms\android
      Package: com.ionicframework.contactosgadc280966
      Name: ContactosGADC
      Activity: MainActivity
      Android target: android-24


      Subproject Path: CordovaLib

      Android project created with cordova-android@6.0.0


      Installing "cordova-plugin-console" for android

      ANDROID_HOME=C:\Users\RODRI\AppData\Local\Android\sdk
      JAVA_HOME=C:\Program Files\Java\jdk1.8.0_101

      Subproject Path: CordovaLib

      Error: no se ha encontrado o cargado la clase principal Dorg.gradle.daemon=true


      Failed to install 'cordova-plugin-console':Error: cmd: Command failed with exit
      code 1
      at ChildProcess.whenDone (C:\xampp\htdocs\ContactosGADC\platforms\android\co
      rdova\node_modules\cordova-common\src\superspawn.js:169:23)
      at emitTwo (events.js:106:13)
      at ChildProcess.emit (events.js:191:7)
      at maybeClose (internal/child_process.js:877:16)
      at Process.ChildProcess._handle.onexit (internal/child_process.js:226:5)


      Error: cmd: Command failed with exit code 1



      C:\xampp\htdocs\ContactosGADC>

      Borrar
    3. Cordial saludo:

      Acabo de hacer algunas pruebas y logré replicar el error dejando como valor de la variable de entorno "Dorg.gradle.daemon=true", sin el guión (o signo menos) al principio.

      Por favor intente dejar como valor "-Dorg.gradle.daemon=true".

      El guión es importante ya que le indica a la máquina virtual que se debe interpretar ese parámetro como una variable de entorno o del sistema para la ejecución del programa actual: http://docs.oracle.com/javase/jndi/tutorial/beyond/env/source.html#SYS

      Espero sea de ayuda.

      Borrar