redhat-developer / vscode-java

Java Language Support for Visual Studio Code
Eclipse Public License 2.0
2.08k stars 441 forks source link

Performance - TTFI for Reopening Previously Imported Java Projects in VS Code #3852

Open mamilic opened 2 weeks ago

mamilic commented 2 weeks ago

I really appreciate all the effort put into supporting Java in VS Code. I was wondering if there are any improvements that could be made to enhance the time to first interaction (TTFI) when reopening projects that have already been imported, rather than on the initial import?

mamilic commented 1 week ago

Hi @rgrunber,

Really appreciate your willing to explain and interact. Thanks for that!

This is my understadning, as far as I can tell the import process consists of 2 steaps.

  1. Importing Maven/Gradle Project, https://github.com/eclipse-jdtls/eclipse.jdt.ls/blob/f155e1fc90618b558899c3722606feccfc89e210/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/ProjectsManager.java#L219-L243, though it might be that it is always executed due to this at least for gradle, https://docs.gradle.org/current/userguide/configuration_cache.html?
  2. Code Indexing, https://github.com/eclipse-jdtls/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java#L326

Is there any steps that can be improved here, as I see that these 2 steps are always fully executed, regardless whether the project has already been imported. Though, there might be more steps, please do correct me.

rgrunber commented 1 week ago

We often come back to looking at ways of improving startup times. There are basically 3-ish phases of going from the moment a project is opened in the workspace, to having vscode-java be fully ready to interact with it. I'll use JDT-LS itself as an example when importing from scratch since it's larger. (It should be noted that there are certain interactions you can perform even while certain operations are still happening) :

  1. the language server needs to be brought up, as it is an OSGi (Eclipse) application with a lot of different dependencies. This is usually independent of the project. (eg. for JDT-LS this takes a few seconds at most)
  2. the language server begins the project import, which is mostly about generating/discovering the necessary metadata about the project. (eg. for JDT-LS project this takes about 1 minute). I think this takes so long because a good amount of time is spent downloading the dependencies.
  3. the project gets built (eg. for JDT-LS project this takes about 30 seconds)

If you take a project that has never been imported, or run Java: Clean Java Language Server Workspace from the command palette, it should always take longer than if you imported a project that you already imported. We definitely avoid rebuilding a project if we detect it has already been done, and so the language server merely tries to import it, which takes a much shorter time.

If I just re-load the VS Code window (as if I just re-opened the JDT-LS project), it takes about 4 seconds for the project import and another 3 seconds for the build phase. In other words, much quicker.

One of the improvements to indexing we made was java.sharedIndexes.enabled (defaults to auto, so only enabled in VS Code Insiders) & java.sharedIndexes.location. These index all libraries in a common location, and since dependencies can overlap locally, it saves time on avoiding the need to rebuild the same library (eg JDK 21) for each project. With that said, the indexing happens in parallel, and doesn't actually contribute to import/build time. If you didn't have the index, you'd still get errors/warnings and I'm pretty sure completion would still work. Just that certain search operations would block until indexing was complete. So it doesn't really contribute to import time.

Update: I tried with https://github.com/spring-projects/spring-petclinic (imported through Gradle build metadata) and observed about the same. Took about 40s to initialize/import and another 30s to build. Reloading the project took about 3s and an additional 6s for the build. If you really wanted to cut down on time, I guess you could try disabling the autobuild (java.autobuild.enabled: false).

mamilic commented 1 week ago

@rgrunber , thank you for explanation! I've tried the peclinic as well and the results are. With Shared indices/indexes turned on, and auto build is disabled. I was wondering if there are some setting to adjust to get your results? 😄

This is run on Arch Linux, Lenovo ThinkPad L14 Gen 1 40.0 GiB RAM AMD Ryzen™ 5 PRO 4650U with Radeon™ Graphics × 12

Import

!ENTRY org.eclipse.jdt.ls.core 1 0 2024-11-14 16:31:16.502
!MESSAGE Workspace initialized in 46473ms

Reload/Reopen

!ENTRY org.eclipse.jdt.ls.core 1 0 2024-11-14 16:32:08.987
!MESSAGE Workspace initialized in 22568ms
rgrunber commented 1 week ago

I would think you should be getting better performance than me. I've only got a Dell Inc. XPS 13 9370, Intel® Core™ i7-8650U × 8, with 16 GiB RAM on Fedora 40.

Are you able to share what the full language server logs look like from reload to finishing ? You could also add "java.trace.server": "verbose" to your setting to get more information. I noticed that the Gradle daemon remains up as a background process even when I close the project, and if I terminate that process, it does add some time to the import, but only about 6s extra, which doesn't account for what you're seeing.

mamilic commented 1 week ago

@rgrunber , I run with verbose enabled, jdtls.log But have no clue where to look at. :/

rgrunber commented 1 week ago

I'm seeing the exact same thing now that I started using the vscode-gradle extension (in combination with vscode-java). Note that JDT-LS has an embedded Gradle build support, but I believe for correctness the Gradle build server (which integrates with vscode-java) can be better, along with more overall support for other Gradle features.

Using this basic setup, on spring-petclinic, I think a full build took about 1min while a simple reload took around 30s. That's probably closer to what you're seeing. I tried with "gradle.allowParallelRun": true but maybe that improved things for the initial build.

@jdneo , is the Gradle build server likely to be a bit slower than Buildship in project import ?

mamilic commented 1 week ago

@rgrunber , what are differences between buildship and vscode-gradle? I've noticed that buildship outputs the build files into the "bin" folder, and plain gradle build outputs to "build" folder. Also, the structure of those is different. Is there a way to setup buildship to behave as plain gradle?

rgrunber commented 1 week ago

https://github.com/redhat-developer/vscode-java/issues/3260#issuecomment-1700516059

In short, the new approach will delegate the build job to Gradle.

I think using vscode-gradle is just closer to the expected output from commandline. T[the 'bin' folder issue is also mentioned as something vscode-gradle resolved.