Gradle Daemon Support for Faster Compilation

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:

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!

About Andrey Breslav

Andrey is the lead language designer of Kotlin at JetBrains.
This entry was posted in Tools. Bookmark the permalink.

35 Responses to Gradle Daemon Support for Faster Compilation

  1. Kirill Rakhman says:

    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. 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:

    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

  4. Eder Bastos says:

    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.

  5. 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:

      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:

    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:

      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:

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

      • Sergey Ryabov says:

        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:

        With 0.12.1218 everything works as intended!

        • Michael Nedzelsky says:

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

          • 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:

            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?

    • Michael Nedzelsky says:

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

  7. Sergey Igushkin says:

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

  8. Ioannis Tsakpinis says:

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

  9. Tom W says:

    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:

    7967 ncss

    0.12.613 91 seconds
    0.12.1230 30 seconds !!!

    :-)

  11. Rostislav says:

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

  12. Amir Abiri says:

    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:

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

    • Dmitry Jemerov says:

      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:

    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:

      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:

    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.

Comments are closed.