K2 is the new frontend for the Kotlin compiler. The compiler is already available for preview and is expected for the future release of Kotlin 2.0.
Last week, the official Android Developer blog published another article encouraging to start testing the new compiler. In this article, we will share the results of using K2 in the project nowinandroid.
The project
- nowinandroid
- Commit: 0a20cb7 (Aug 2, 2023)
- Gradle 8.2
- AGP 8.1.0
- KGP 1.9.0
Scenarios
We have defined two main scenarios:
- Clean builds
- Incremental builds
The two variants used in both scenarios are main(branch) and k2, which is based on the main branch adding in gradle.properties:
kotlin.experimental.tryK2=true
Clean Builds
For each variant, we executed 100 parallel builds in GitHub runners for the task test
using Gradle Enterprise API to retrieve the data.
The build execution for both variants was:
Main | K2 | |
---|---|---|
Mean | 15m 23s | 15m 22s |
Median | 15m 28s | 15m 21s |
We didn't notice a significant difference. We understood it could be caused by the different tasks involved in the build execution. In the end, K2 affects only a subset of tasks in the build. We continued analyzing more in detail by aggregating the duration of the Kotlin-related tasks by type(ms):
Type | Main | K2 | |
---|---|---|---|
KotlinCompile | Mean | 2818 | 2800 |
KotlinCompile | Median | 1682 | 1812 |
KaptGenerateStubsTask | Mean | 853 | 1067 |
KaptGenerateStubsTask | Median | 515 | 669 |
KaptWithoutKotlincTask | Mean | 1049 | 1106 |
KaptWithoutKotlincTask | Median | 1682 | 1812 |
Still, the aggregation didn't show juicy data.
It's important to highlight that KSP and KAPT tasks currently fall back to using the old compiler when building your project with K2:
:feature:foryou:kaptGenerateStubsDemoReleaseKotlin
w: Kapt currently doesn't support language version 2.0+.
Falling back to 1.9.
For this reason, we focused only on the Kotlin Compiler task results.
We needed to have more detail in the analysis, so we picked the DemoDebug
build variant and analyzed the median by task path:
At this point, we started seeing differences for specific tasks, in some cases positives, in others negative.
Taking those results, we represented the % of the duration difference relative to the main branch:
That gave us more hints to follow. To discard a possible outlier, we verified the worst and best results in the measurement:
- ui-test:hilt-manifest
- core:datastore
It looked good.
To understand better the origin of the differences, we used Kotlin Build Reports. This feature lets us get more detailed information about Kotlin compiler metrics. To enable it, add the property in gradle.properties:
kotlin.build.report.output=build_scan
Once we enable Kotlin Build Reports, a custom value is created in the Build Scan with the compiler information:
We aggregated the data of Kotlin Build Reports and provided the median for the tasks under investigation, worst-case:
:ui-test-hilt-manifest
metrics gave significantly worse results for:
- Compiler code analysis
- Compiler code generation
- Run compilation in Gradle worker
- Sources compilation round
This module is used only in debugImplementation
on the main app for the tests calling Composables that hook into the Dagger graph configuration. Additionally, I suspected that the kapt/hilt configuration could affect also.
core:datastore:compileDemoDebugKotlin
decrease the duration of:
- Code generation lines per second
- Compiler code analysis
- Run compilation in Gradle Worker
- Sources compilation round However, we observed an increase in duration for:
- Compiler code generation
This module represents better a real Android module in our codebase, and we can confirm we got a 25% reduction in duration time for the task using K2. Great!
To finish this section, I found it interesting to share the metrics for other modules in the project using Kotlin Build Reports:
Incremental Builds
Using Gradle Profiler, we defined a scenario where we applied an incremental change to the class core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt
.
Each variant executed 50 builds for the task testDemoDebugUnitTest
.
The results of analyzing the overall build time were:
Main | K2 | |
---|---|---|
Mean | 1m 4s | 1m 20s |
Median | 1m 1s | 1m 16s |
We were slightly increasing the overall build time. We repeated the same process of the previous scenario aggregating duration grouped by the Kotlin Compiler task:
Type | Main | K2 | |
---|---|---|---|
KotlinCompile | Mean | 1267 | 1891 |
KotlinCompile | Median | 1033 | 1290 |
The gap between the mean and median led us to think that a performance issue occurred occurring during the K2 variant. We retrieved the GC time of the Kotlin process and confirmed the hypothesis:
Before pointing to a memory leak caused by using K2, it's fair to mention that these experiments are executed in free GitHub runners with limited resources. The available memory is 7 Gb using 3 Gb for each process(Kotlin/Gradle).
We reduced the number of iterations to 10 in the K2 variants and continued the investigation. After reducing the iterations, the results were:
Main | K2 | |
---|---|---|
Mean | 1267 | 1284 |
Median | 1033 | 1076 |
This time, we have closer results. Then we visualized the data grouped by task path:
Representing the data by % Diff:
The K2 got better times in all the tasks of the incremental build except for :feature:interest
and :app
.
Using Kotlin Build Reports, we aggregated the task metrics for the best and worst tasks in the % diff results:
:core:data
, the module that applies the incremental change, shows a decrease in duration for:
- Compiler code analysis
- Run compilation in Gradle Worker
- Sources compilation round However, we observed an increase in duration for:
- Compiler code generation
:feature:interest
showed a 10% increase in aggregated build time. After analyzing the module, we didn't find a proper explanation of the behavior of this specific module compared with other feature modules.
Final Words
K2 is here and looks promising.
The project under experimentation doesn't represent a production project. The results in other projects may differ.
KAPT/KSP falling back to the old compiler may affect the final results, and we could expect better results once they are compatible.
So far, we have seen in this article that using K2 significantly improves Kotlin Compiler build time.
Happy Building!
Top comments (0)