-
-
Notifications
You must be signed in to change notification settings - Fork 778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detekt gradle is much slower than the jar #2035
Comments
This is a known problem (though we might not specifically have an issue open for it). There is a cost to setting detekt up for each module when run over a large Gradle project that you don't see when it's run as a single invocation on the CLI. Gradle's worker API may help but I haven't investigated in any detail. |
Does it have sense to be that heavy? Maybe is something that we can fix. And if it can't be optimized we could do something to pay it only once. I'm curios about why the non parallel work is just 1.5 times slower and no ~8 times I'm going to expend some time researching this. Any idea is welcome! |
Ok, I moved to an open soruce project to do all the metrics so I can share all the data. I peaked: LeakCanary I created a fork and update detekt to the last version and that's what I'm using to get this numbers
A gradle scan: I took the time of each task and then substract the time that detekt reports in each log:
The average is that we are using 1200ms "doing something that it's not checking rules" per task. I checked with The total time that we use checking rules is 56,243ms. But running |
Be aware that type resolution is only used in Gradle when running one of the auto-generated tasks like |
Great, so my numbers are more acurate. I'm having big troubles profiling a jvm application. Profile an Android app is much simplier. For now I checked that this code takes around 150ms per module: // DetektInvoker.kt
val proc = project.javaexec {
it.main = DETEKT_MAIN
it.classpath = classpath
it.args = listOf("@${argsFile.absolutePath}")
it.isIgnoreExitValue = true
} That's the 10% of the time if we think in the non-parallel case. What do you think about add And probably we'll decrease the time more than 10%. Because we woudn't need to load all the classes per each module. They would be load just for the first module. |
Okay, I wrote some TERRIBLE code just to probe my theory: 1c2e586. I'm not an gradle expert and I don't understand how the detekt gradle plugin works exactly. But with this change the command How to test this code:
Now you have all the things you need builded. So to measure you just need to:
And if you want to know what was before just use the last two commands. So, with this data I think that we should consider to change how |
@BraisGabin thanks for the investigation and the late response! I think the idea behind removing the hard dependency on Be aware that #1991 now merged can also inpact the gradle plugin performance when using |
Sure, we can check this after that PR.
I understand that but I think that the performance impact is far too big here. I have an idea but I don't know if we can do it with gradle: We can add the dependency to This is an idea. If we can do it, perfect. If it's not posible I belive that we should think about removing this feature to get that performance boost because it's huge.
I'll try it, but I think that the bottleneck right now is how we launch |
A simple workaround would be to create a custom task that runs over all source in the project at once. That way Gradle only forks once for the entire build instead of once for each module. See The problem with this approach is that rules relying on type resolution will not work, and there will be more of these in future. Also remember that the Gradle plugin supports incremental builds so once it's run once for each module it won't run again for that module unless needed. Though the first build will take a little while it will be much quicker after that, especially for multi module builds. |
But this will also work when we hardcode the cli version for the gradle plugin. I ping @marschwar for his gradle knowledge and hope he can take part in this discussion :) |
Will there be classpath issues if detekt CLI is run in the same process as Gradle itself? Detekt and Gradle both depend on embedded Kotlin compiler. Any change also has to make sure the project version of CLI can be used by the Gradle plugin so it's easy to still run detekt over itself using Gradle, or to use the Gradle plugin as an included build in other projects so changes in rules/CLI can be tested easily in other projects. This is very convenient when testing rules. As long as there's a workflow that still works for development and we know there won't be classpath conflicts then I support this change. |
I played around with it a little bit and I have to admit, the difference in performance is huge. Great find @BraisGabin.
While testing I excluded excluded everything with org.jetbrains.kotlin from the cli-all.jar and that did get rid of the warning that kotlin is on the path multiple times. But I don't know enough about classpaths with gradle plugins to say how this would play out. |
The chickend/egg problem I think that you can fix it depending on the cli jar itself. It's not perfect because you need to assemble first and then use it.... But it works. kotlin-mockito does that. I'm not a huge fan but if we want this feature, it's an option. The main problem that I see here is the classpath... I'm just pointing some ideas that I have:
|
I just saw the issue #39. It should help here. |
…#2035 This deprecates setting the detekt version via the toolVersion property of the extension.
The way we bootstrap the gradle plugin now, brings a lot of complexity to our build and even slows down contributions. Having the gradle plugin in this repository does not help with faster releases too. As we now have our organization I propose to move the gradle plugin to it's own repository after 1.3.1. Merge #2200 in that repo and profit from faster build and ci times in the main detekt repo :). |
I've done this for the compiler-plugin prototype, excluded the embedded compiler and it seems to work. Gradle itself comes with a file We are using the shadowed stuff a lot |
…#2035 This deprecates setting the detekt version via the toolVersion property of the extension.
…#2035 This deprecates setting the detekt version via the toolVersion property of the extension.
…#2035 This deprecates setting the detekt version via the toolVersion property of the extension.
I've written down my observations of inspecting this issue in #2282. |
I will release a beta/test version of 1.7.0 today so we can get some feedback for this. |
./gradlew detekt
is much slower thanjava -jar ~/projects/detekt/detekt-cli/build/libs/detekt-cli-1.1.1-all.jar -c detekt-config.yml -b baseline.xml
in multi-module projects.I did some measure with a project where I have gralde detekt installed (I can't share it because is the code of my employer).
./gradlew detekt
withorg.gradle.parallel=true
(8 workers)./gradlew detekt
withorg.gradle.parallel=false
java -jar ~/projects/detekt/detekt-cli/build/libs/detekt-cli-1.1.1-all.jar -c detekt-config.yml -b baseline.xml
This project has 44 modules right now.
Any idea why is this happening? How could we improve this numbers?
The text was updated successfully, but these errors were encountered: