Gradle Daemon Support for Faster Compilation

Posted on by Andrey Breslav

We are currently working on improving compilation times. Today we are happy to invite you to try Kotlin 0.12.1230 making use of the Gradle Daemon. It eliminates startup costs, and your builds run faster.

Background

Among other things, loading classes of the compiler and warmup activities of the JVM seem to contribute a lot to the time it takes kotlinc to run. This is why we are looking into ways of using the same compiler instance over and over again: no need for loading gives better compilation times.

Since other tools running on the JVM seem to suffer from the same issues, there’s substantial infrastructure facilitating such things. Gradle has its Daemon, a long-running process (actually, it can be many processes) whose essential function is to keep tools loaded and therefore run them without the startup costs of class loading and JIT-compilation.

Try it out

We have fixed some issues that prevented Kotlin to leverage this functionality. It works reliably in Gradle 2.4 and higher (for Gradle upgrade instructions see Gradle docs). Android Studio uses the daemon by default, so you don’t have much to do, simply specify Kotlin version “0.12.1230” in your build.gradle file:

buildscript {
  repositories {
    ...
  }
  dependencies {
    classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.12.1230'
    ...
  }
}

NOTE: we get the full-scale speedups only after a few runs of the build. The first time we run cold and wait for the warmup, the second time most of the warmup is gone, and the build completes faster. Subsequent runs may get slightly faster too, because of the JIT.

Feedback

Please tell us if your builds have gotten faster with this change. Some project details (like LOCs and actual build times) would be appreciated.

Thanks!

Comments below can no longer be edited.

35 Responses to Gradle Daemon Support for Faster Compilation

  1. Kirill Rakhman says:

    August 5, 2015

    That’s amazing! With 0.12.1218, compiling Kotlin classes took around 14 s while with 0.12.1230, after some warming up, the compilation takes < 6 s. I guess the jitter is finally able to optimize the hell out of the Kotlin compiler.

  2. Oleg (Kotlin fan) says:

    August 5, 2015

    This is so awesome!
    The compilation time is a headache for Android developers now.
    I hope this new version will reduce the time developers should waste on waiting for the project compilation (front-end developers have to recompile a project pretty often).

  3. Paweł says:

    August 5, 2015

    Rebuild:

    Version 0.12.200:
    Compilation completed successfully in 1m 41s 722ms (~100 seconds)

    Version 0.12.1230:
    Compilation completed successfully in 1m 22s 623ms (~80 seconds)

    Feedback is positive

    • Andrey Breslav says:

      August 5, 2015

      It may get faster in subsequent runs, actually

      • Kirill Rakhman says:

        August 5, 2015

        Andrey, you could probably note that in the blog post.

  4. Eder Bastos says:

    August 5, 2015

    I’m seeing similar performance improvements to what’s been described by the other commenters. Unfortunately, this seems to have broken my usage of Dagger2.

    Most of my app is written in Kotlin, but I must initialize my main AppComponent in a Java class, because of the limitations surrounding generated code visibility. With 0.12.1230, a clean build of my project works fine, but subsequent builds fail, complaining that the generated implementation of my AppComponent (DaggerAppComponent) can’t be found. Android Studio can find it, and it doesn’t indicate an error in the file itself – it just fails at compile-time.

    In addition, the following error is displayed by the compiler:
    warning: The following options were not recognized by any processor: '[kapt.annotations]'

    Again, building from a clean state works well – but doing a clean build every time will erase this update’s performance benefits, so I’ll have to roll back until the issue is fixed.

    • Andrey Breslav says:

      August 5, 2015

      Thanks for the report, we’ll look into this

    • Eder Bastos says:

      August 5, 2015

      Actually, it appears this issue was also present in 1218 (I had been on 613 until now.) So if you’re reading this, and didn’t already have such a problem, feel free to upgrade to 1230 🙂

      Issue report has been filed here: https://youtrack.jetbrains.com/issue/KT-8733

    • Steve Holmes says:

      August 6, 2015

      You don’t necessarily have to roll back. You could use the –no-daemon option when running gradle.

  5. Artem Zinnatullin says:

    August 5, 2015

    I have problem with building library project with Android Gradle Plugin 1.3.0 and Kotlin Gradle Plugin 0.12.1230:


    lint FAILED

    FAILURE: Build failed with an exception.

    What went wrong:
    Execution failed for task ':lib:lint'.
    > class com.android.tools.lint.client.api.ClassEntry$SuperclassVisitor has interface org.objectweb.asm.ClassVisitor as super class

    Exception:


    * Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':lib:lint'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:310)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:88)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:68)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:55)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:149)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:90)
    at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:49)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.health.DaemonHealthTracker.execute(DaemonHealthTracker.java:47)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:66)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:71)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.health.HintGCAfterBuild.execute(HintGCAfterBuild.java:41)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:246)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
    Caused by: java.lang.IncompatibleClassChangeError: com/android/tools/lint/client/api/ClassEntry$SuperclassVisitor
    at com.android.tools.lint.client.api.ClassEntry.createSuperClassMap(ClassEntry.java:277)
    at com.android.tools.lint.client.api.LintClient.createSuperClassMap(LintClient.java:833)
    at com.android.tools.lint.detector.api.Project.getSuperClassMap(Project.java:1297)
    at com.android.tools.lint.client.api.LintClient.getSuperClass(LintClient.java:799)
    at com.android.tools.lint.client.api.LintDriver$LintClientWrapper.getSuperClass(LintDriver.java:1984)
    at com.android.tools.lint.client.api.LintDriver.getSuperClass(LintDriver.java:1169)
    at com.android.tools.lint.client.api.LintDriver.isSubclassOf(LintDriver.java:1198)
    at com.android.tools.lint.checks.ClickableViewAccessibilityDetector.checkClass(ClickableViewAccessibilityDetector.java:95)
    at com.android.tools.lint.client.api.AsmVisitor.runClassDetectors(AsmVisitor.java:153)
    at com.android.tools.lint.client.api.LintDriver.runClassDetectors(LintDriver.java:1368)
    at com.android.tools.lint.client.api.LintDriver.checkClasses(LintDriver.java:1262)
    at com.android.tools.lint.client.api.LintDriver.runFileDetectors(LintDriver.java:1050)
    at com.android.tools.lint.client.api.LintDriver.checkProject(LintDriver.java:882)
    at com.android.tools.lint.client.api.LintDriver.analyze(LintDriver.java:433)
    at com.android.tools.lint.client.api.LintDriver.analyze(LintDriver.java:374)
    at com.android.tools.lint.LintCliClient.run(LintCliClient.java:128)
    at com.android.build.gradle.internal.LintGradleClient.run(LintGradleClient.java:112)
    at com.android.build.gradle.internal.LintGradleClient$run.call(Unknown Source)
    at com.android.build.gradle.tasks.Lint.runLint(Lint.groovy:203)
    at com.android.build.gradle.tasks.Lint.this$5$runLint(Lint.groovy)
    at com.android.build.gradle.tasks.Lint$this$5$runLint$5.callCurrent(Unknown Source)
    at com.android.build.gradle.tasks.Lint.lintAllVariants(Lint.groovy:94)
    at com.android.build.gradle.tasks.Lint$lintAllVariants$4.callCurrent(Unknown Source)
    at com.android.build.gradle.tasks.Lint.lint(Lint.groovy:82)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:226)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:219)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:208)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:589)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:572)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)

    • Michael Nedzelsky says:

      August 12, 2015

      Can you provide more information (content of build.gradle and/or list of other libraries in your project) or an example of Android project in order to reproduce this error?

  6. Marco Rodriguez-Suarez says:

    August 5, 2015

    I’m seeing the same error as Artem for multiple Android projects with the Android Gradle Plugin version 1.2.3 and 1.3.0:

    FAILURE: Build failed with an exception.

    What went wrong:
    Execution failed for task ‘:app:lint’.
    > class com.android.tools.lint.client.api.ClassEntry$SuperclassVisitor has interface org.objectweb.asm.ClassVisitor as super class

    • Michael Nedzelsky says:

      August 12, 2015

      Can you provide more information (content of build.gradle and/or list of other libraries in your project) or an example of Android project in order to reproduce this error?

    • Zalim Bashorov says:

      August 12, 2015

      Additionally could you, please, try to reproduce it with prev kotlin plugin (0.12.1218)?

      • Sergey Ryabov says:

        August 13, 2015

        Did you manage to reproduce this error? Or maybe already solved the issue? I can provide feedback if needed, because I’ve just encountered similar fail when building release build of my Android app. Only that mine happened during app:shrinkReleaseResources task.

        Execution failed for task ‘:app:shrinkReleaseResources’.

        class com.android.build.gradle.tasks.ResourceUsageAnalyzer$UsageVisitor has interface org.objectweb.asm.ClassVisitor as super class

      • Sergey Ryabov says:

        August 13, 2015

        With 0.12.1218 everything works as intended!

        • Michael Nedzelsky says:

          August 13, 2015

          No, I still could not reproduce this error. Any additional information will be useful (or an example project).

          • Artem Zinnatullin says:

            August 13, 2015

            I can not give you sources of the app, and I don’t know what is causing the problem.

            But I can say that we use Java annotation processing, may be it’s breaking the build with new plugin?

          • Sergey Ryabov says:

            August 16, 2015

            I cannot share my code either, but I can tell that we are using Lombok & Retrolambda in the Java part of the app. May it be that any of them conflicts with Kotlin?

            • Andrey Breslav says:

              August 16, 2015

              Well, since it’s a problem in your build, could you share the build file?

    • Michael Nedzelsky says:

      August 17, 2015

      You can look at https://youtrack.jetbrains.com/issue/KT-8852 for explanation and possible workaround.

  7. Sergey Igushkin says:

    August 6, 2015

    The same to me, about 2x speedup: a project with ~2900 Kotlin LOC has sped up from 15s to 8s.

  8. Ioannis Tsakpinis says:

    August 6, 2015

    0.12.218: 1m 23s
    0.12.230: 1m 2s (after a few builds)

  9. Tom W says:

    August 6, 2015

    It’s great to hear Kotlin compilation time is getting additional focus. My hope is that the regular IDE compilation (i.e. non-external builds like gradle) speed will also improve to rival that of IDEA’s current groovy-eclipse compiler, where the “code change done->build->compilation done” cycle on my machine, for example, is around 2 seconds for groovy files and around a second for Java files. Once the compilation time become negligible, the code/test or code/redeploy-with-something-like-jrebel cycle really starts to flow.

  10. Michael Zehender says:

    August 6, 2015

    7967 ncss

    0.12.613 91 seconds
    0.12.1230 30 seconds !!!

    🙂

  11. Rostislav says:

    August 6, 2015

    Kotlin task goes from 10-11 secs to 4 on a small project. Nice!
    Andrey, what plans do you have about Jack & Jill?

    • Andrey Breslav says:

      August 6, 2015

      No particular plans at the moment, but we’ll need to find a way to integrate.

  12. Amir Abiri says:

    August 9, 2015

    I wonder if Gradle itself would have run faster if it was ported to Kotlin, which, with its support for functional idioms, can support the same DSL features (if not more).

  13. Roman Chernyatchik says:

    August 12, 2015

    I cannot find 0.12.1230 plugin for IDEA 15 EAPs, it is available?

    • Dmitry Jemerov says:

      August 13, 2015

      The changes in 0.12.1230 affect only the Gradle plugin; you don’t need to update your IntelliJ plugin to use them.

  14. Vitaliy says:

    August 13, 2015

    I’m using kotlin in my android pet project (idea 14.1.4 + kotlin 0.12.1230). And I’m stuck in a rut with kotlin code changes appear on device ui only after rebuild whole project. Is it only my local issue? How can I fix it?
    Thanks.

    • Vitaliy says:

      August 15, 2015

      Fixed by updating gradle (from 2.2.1 to 2.6) and android gradle plugin (from 1.1.2 t0 1.3.1)

  15. Ivans says:

    August 31, 2015

    This version definitely speeds up the build time probably to 30% for me. But…

    I have an issue with the kapt tool. One of the android library project uses Realm DB, and proxies classes are generated (the same way as for Dagger) during build. Output aar file for this module in classes.jar contains these generated classes. But they are missing for android client project (checked folder build/intermediates/exploded-aar/../../unspecified/jars/classes.jar/io/realm/) and as a result have Realm complain that it can’t find those classes. Not sure what the cause of such issue, previous kotlin version (for instance 0.12.613) was working fine, also full clean and rebuild helps, please advice.

Subscribe

Subscribe for updates