如果将应用程序从Java转换为Kotlin,编译时间会更长吗?
这是 Kotlin 系列文章的第三部分。第一部分讨论了将 Android 应用程序从 Java 转换为 Kotlin,第二部分包含我对 Kotlin 语言的看法。
在早些时候的一篇文章中,我讨论了将 Android 应用程序从 Java 转换为 100% Kotlin。Kotlin 代码库比其 Java 前身更小更易维护,因此我得出结论,这种转换是值得的。但有些人不想尝试 Kotlin,因为他们担心它的编译速度可能不如 Java。这绝对是一个合理的关注点;如果将代码库转换为 Kotlin 会导致长时间的构建时间,那么没有人愿意花费时间进行转换。因此,让我们来看看将 App Lock 应用程序 从 Java 转换为 Kotlin 后的编译时间差异。我不会尝试比较单行 Kotlin 与单行 Java 的速度;相反,我将尝试回答将代码库从 Java 转换为 Kotlin 是否会影响其整体构建时间的问题。
我如何测试构建时间
我编写了 shell 脚本,在各种场景下重复运行 Gradle 构建。所有测试均连续进行 10 次。在每个场景中都清除了项目,并对使用 Gradle 守护进程 的场景,在对该场景进行基准测试之前停止了守护进程。
本文中的所有基准测试都是在 Intel Core i7–6700 运行,运行速度为 3.4 GHz,拥有 32GiB 的 DDR4 内存和 Samsung 850 Pro SSD 的情况下进行的。源代码使用 Gradle 2.14.1 构建。
测试
我想在几种常见的使用情况下运行基准测试:干净的构建,有和没有 Gradle 守护进程的增量构建以及有更改的增量构建。
在转换之前,App Lock 的 Java 代码库为 5,491 个方法和 12,371 行代码。重写后,这些数字降至 4,987 个 Kotlin 代码方法和 8,564 行。在重写期间没有发生重大的架构更改,因此在重写之前和之后测试编译时间应该可以很好地说明 Java 和 Kotlin 之间的构建时间差异。
没有 Gradle 守护进程的干净构建
这是两种语言中构建时间最长的情况:从冷启动运行干净的构建。对于这个测试,我禁用了 Gradle 守护进程。
以下是十个构建的耗时:
没有 Gradle 守护进程的十个连续干净构建
在这种情况下,Java 的构建时间平均为 15.5 秒,而 Kotlin 的平均时间为 18.5 秒:增加了 17%。Kotlin 的开端并不是很好,但这不是大多数人编译代码的方式。
对于没有 Gradle 守护进程的干净构建,Java 比 Kotlin 编译快 17%。
重复编译相同的代码库并对其进行更改更为常见。这是 Gradle 守护进程设计的一种情况,因此让我们看看在使用它时数字的变化。
使用 Gradle 守护进程的干净构建
JIT 编译器(如 JVM)的问题在于,它们需要时间来编译在它们上执行的代码,因此随着进程的运行,其性能会提高。如果停止 JVM 进程,则会失去性能增益。在构建 Java 代码时,你通常会在每次构建时启动和停止 JVM。这迫使 JVM 在每次构建时重新执行工作。为了解决这个问题,Gradle 带有一个守护进程,它将在构建之间保持活动状态,以保持 JIT 编译的性能增益。你可以通过在命令行上传递 --daemon,或者在 gradle.properties 文件中添加 org.gradle.daemon=true 来启用守护进程。
以下是相同一系列干净构建的情况,但守护进程正在运行:
使用 Gradle 守护进程运行的十个连续干净构建
如你所见,第一次运行所需的时间与没有守护进程的时间大致相同,但后续运行的性能会提高,直到第四次运行。在这种情况下,查看第三次运行后的平均构建时间更有用,因为此时守护进程已经启动。对于热运行,Java 的干净构建平均时间为 14.1 秒,而 Kotlin 的时间为 16.5 秒:增加了 13%。
对于使用启动的 Gradle 守护进程的干净构建,Java 比 Kotlin 编译快 13%。Kotlin正在追赶Java,但仍然略逊一筹。然而,无论使用什么语言,Gradle守护进程都将缩短编译时间超过40%。如果你还没有使用它,现在应该开始使用了。
因此,对于全面构建,Kotlin的编译速度比Java略慢一些。但通常情况下,你只会在对几个文件进行更改后编译,而增量构建将具有不同的性能特征。因此,让我们看看Kotlin是否能在最重要的地方追赶上。
增量构建
编译器的最重要的性能特征之一是其增量编译的使用。普通构建将重新编译项目中的所有源文件,但增量构建将跟踪自上次构建以来更改的文件,并仅重新编译那些文件及依赖它们的文件。这对于编译时间尤其对于大型项目而言,会产生巨大的影响。
增量编译于Kotlin的1.0.2版本中添加,你可以通过在_gradle.properties_中添加_kotlin.incremental=true_或使用命令行选项来启用它们。
那么,当使用增量编译时,Kotlin的编译时间与Java相比如何呢?下面是在没有更改文件的情况下使用增量编译的基准测试结果:
连续进行十次增量编译,没有文件被更改
接下来,我们将测试使用修改后的源文件进行增量编译。为了测试这一点,我在每次构建之前更改了一个Java文件及其Kotlin等效文件。在这个基准测试中,源文件是一个没有其他文件依赖的UI文件:
连续进行十次增量编译,一个隔离的文件被更改
最后,让我们看看在修改被项目中的许多其他文件导入的源文件的情况下进行增量编译的情况:
连续进行十次增量编译,一个核心文件被更改
你可以看到Gradle守护进程仍需要两到三次运行才能热身,但之后两种语言的性能非常相似。在没有更改的情况下,Java需要每个热构建4.6秒,而Kotlin平均需要4.5秒。当我们更改一个其他文件没有使用的文件时,Java需要平均7.0秒进行热构建,而Kotlin则需要6.1秒。最后,当我们更改一个被项目中许多其他文件导入的文件时,一旦Gradle守护进程热身,Java需要7.1秒进行增量构建,而Kotlin平均需要6.0秒。
在最常见的配置——启用增量编译的部分构建中,Kotlin的编译速度与Java一样快,甚至稍快。
结论
我们进行了一些不同的场景基准测试,以查看Kotlin在编译时间方面是否能够跟上Java。虽然Java在全面构建方面比Kotlin快10-15%,但这些情况相对较少。对于大多数开发人员来说,更常见的情况是部分构建,其中增量编译可以大大提高效率。使用运行Gradle守护进程和启用增量编译,Kotlin的编译速度与Java一样快,甚至略快。
这是一个令人印象深刻的结果,也是我没有预料到的。我必须称赞Kotlin团队设计了一种不仅具有许多出色功能,而且可以编译得如此迅速的语言。
如果你因为编译时间而一直不敢尝试Kotlin,那么现在不必担心了:Kotlin的编译速度与Java一样快。
你可以在这里找到我为所有基准测试收集的原始数据。
译自:https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d
评论(0)