microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
161k stars 28.25k forks source link

Extensions using the "type" command (for ex. Vim) have poor performance due to being single-threaded with other extensions #75627

Open DanTup opened 5 years ago

DanTup commented 5 years ago

I don't know if this is a known/accepted issue, but I've had a number of users complain of poor performance in the editor when using my extension along with the Vim extension.

This appears to be because the Vim extension uses the type command to handle keypresses (in the extension host). This means if pressing a key triggers a command that blocks in another extension (for example the first character press can trigger code completion, which if the list is 20,000 items can block the thread for a little while while they're build + serialised) the typing in the editor is really sluggish.

You can easily reproduce this by making an extension that blocks for 1s when asked for completions:

context.subscriptions.push(vscode.languages.registerCompletionItemProvider({ scheme: "file" }, {
    provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
        console.log('Completion request');
        var start = Date.now();
        let i = 0;
        while (Date.now() < start + 10000) {
            // Block for a second...
            i++;
        }
        return [new vscode.CompletionItem(`aaaItem (${i} iterations)`)];
    },
}));

If you run this and enabled the Vim plugin, when you start typing on a newline (which triggers completion), the characters you type won't appear for a while.

Of course, extensions should try to avoid blocking the extension host as much as possible, but sometimes it's unavoidable (for ex. assembling and serialising a huge number of completion items). It's not clear where users should raise bugs, since in isolation neither extension is really doing anything wrong.

I don't know what the fix is (separate extension host for type-handling extensions might work, but that might also be a huge task), but I couldn't find any issues discussing this and figured it was worth some discussion (even if only to have the information described in one place we can point people to that hit these issue).

rebornix commented 5 years ago

Similar issue https://github.com/microsoft/vscode/issues/65876 . @DanTup thanks for the nice write up.

alexdima commented 4 years ago

In general, I agree. But running each extension in its own separate thread has implications such as: text buffer memory duplication or a variant of a text buffer written in C++ that can be snapshotted and shared safely across threads, native node modules loaded by extensions in the ext host which are not multi-process aware, or sync API dependencies between extensions. It also means that the renderer process will need to host N extension hosts (sending messages to N different endpoints) with its perf implications... I don't think we will change this architecture too soon...

Of course, extensions should try to avoid blocking the extension host as much as possible, but sometimes it's unavoidable (for ex. assembling and serialising a huge number of completion items). It's not clear where users should raise bugs, since in isolation neither extension is really doing anything wrong.

The extension that eats 1s from the event loop is definitely doing something wrong and should get a bug. IMHO, 1s is a very very long time to be blocking the event loop, even that of the extension host.

DanTup commented 4 years ago

IMHO, 1s is a very very long time to be blocking the event loop, even that of the extension host.

That's true, but 1s is way longer than is required to make typing feel sluggish. Even 200ms could feel sluggish, and saying we shouldn't block for 200ms across any machines is an impossible task (I've had logs where things that take only 10-20ms on my machine, many hundreds of ms on some users machines).

Even if extensions only ever blocked for shorter periods - some users have tons of them installed, so they could still easily add up.

I understand it's hard to solve, but a lot of time goes into debugging performance issues (both by users and extension authors). I've personally spent a lot of time digging into this (I got a report that typing was super sluggish, and I was under the impression that keystrokes were always handled in the main VS process - it was a lot of digging to figure out what was actually going on - especially as I couldn't repro it for a long time because I didn't know about Vim and how it worked), and I'm certain I'm not the only one.

If it's going to remain like this, maybe there should at least be a warning in the console the first time a keystroke takes longer than ` few hundred ms because it went via the extension host and was slow. That would've saved a lot of time.

alexdima commented 4 years ago

I agree and appreciate your frustration and wasted time on this and I am sorry about it...

I don't have a good answer except to point out that things could be worse... I'm happy extensions are not running with the rest of VS Code on the renderer process... That was one of my contributions to the architecture of VS Code, one which I had to defend quite sternly to more experienced folks coming in with an Eclipse background (where everything is single proc, albeit with multiple threads, but oftentimes stuff ended up on the UI thread...), or looking at Atom or other editors...

The current reality is that extensions today share a single event loop and that everyone needs to play nice (and yield every now and then) for things to work out well. Sharing a single event loop doesn't mean you can't do expensive stuff per-se, it means you shouldn't block for extended periods of time. If you were to yield every 10ms, in the spirit of "cooperative threading", things would work out... In any case, I am not against improving this, by all means I would like to have it better, but please accept that I believe it is difficult and costly to improve now, and it is not at the top of the list priority wise.

We have all kind of automatic warnings when the extension host becomes unresponsive for a certain time, but nothing so small as a few hundred ms... It is also worth keeping in mind that for us, there is no correlation between pressing a keystroke and vim issuing an edit. In other words, typing ends up invoking the type command. By default the type command is implemented by us on the renderer. The vim extension effectively overwrites it and at some later point (async from the point of view of the renderer) issues edits back to the renderer process. We have no idea that those edits have as root cause a certain keypress or that a long time has passed... So it isn't something straight-forward to detect on our side (in the core)... We might detect it if we were to expand the editing API and add some correlation argument that could be passed back to us so we have a chance to correlate the keydown / command invocation with an edit...

I am sorry I don't have better answers, I am open to ideas... For a while I thought that perhaps we should do a special case and have 2 extension hosts, one for vim, and one for everything else...

DanTup commented 4 years ago

I'm happy extensions are not running with the rest of VS Code on the renderer process... That was one of my contributions to the architecture of VS Code

FWIW, I'm also happy about this. It does add some limitations (for ex. the "Flutter UI Guides" in IntelliJ are synced perfectly to editing), but I hate a laggy editor - that's why I opened this issue to start a discussion - it feels like a backdoor :-)

please accept that I believe it is difficult and costly to improve now, and it is not at the top of the list priority wise

Ofc, I didn't mean to sound like I thought you had to fix this - but I think it would be a shame to just close it "as designed" without thinking about options (even if they're way down the list).

there is no correlation between pressing a keystroke and vim issuing an edit. In other words, typing ends up invoking the type command. By default the type command is implemented by us on the renderer. The vim extension effectively overwrites it and at some later point (async from the point of view of the renderer) issues edits back to the renderer process

Good point - I thought you could wait until the command finished executing (I presume you know when that happens) - but it didn't occur to me that it could do its own async work and return early :(

For a while I thought that perhaps we should do a special case and have 2 extension hosts, one for vim, and one for everything else

That's what I meant with "separate extension host for type-handling extensions might work" above. Assuming only one extension can take it, it could be an additional host just for the type-handling extension. It would be better if it wasn't Vim-specific ofc (which might mean you'd need something in the manifest to declare that it handles typing - which might not be a bad thing to ensure the user doesn't have two of them).

Since you said "For a while", I presume you no longer think that? It does add complexity, but if supporting these kinds of extensions is important, it doesn't sound completely crazy to me (with my very limited understanding of VS Code :-)). It would solve the "who should fix this?" confusion with current behaviour - Vim should understandably be upset if other extensions hog the thread and make them perform badly, but it's easy for other extensions to argue "well you delegated your key presses to the extension host, you gotta deal with added latency during typing".. If it's not really obvious where the fault lies, it's likely not to be fixed by either.

xconverge commented 4 years ago

I am pretty bummed that we have brought the Vim extension to where it is now, and are constantly bombarded with performance issue reports. It is almost "good enough" but this caveat is enough to turn a percentage of users away from the experience and it is a bit demoralizing at times to have no path forward.

Thanks for the explanations in this issue report though, the comments have cleared up a few things for me.

DanTup commented 4 years ago

The Rust analyzer extension also uses this command:

https://github.com/rust-analyzer/rust-analyzer/issues/2331

I suspect its use will become more common as extensions want to provide additional functionality that the VS Code API doesn't support. I think this problem is only going to get worse.

Maybe as a start, VS Code should prompt users when an extension registers this (like it does when an extension tries to override PATH in the terminal) so there's least have some visibility that the extension does something that could significantly impact editor performance? For Vim it might be more obvious it's doing something like this, but for something like Rust I think it's far less obvious.

jedwards1211 commented 4 years ago

However desirable it may be in theory to run extensions in a separate process, in practice, the way that was implemented in VSCode has resulted in far worse and less likely to get fixed Vim emulation performance than all of the following IDEs:

When it comes to user experience I prefer IDEs that take the risk of running extensions in process, even when buggy extensions have ended up crashing the process.

jedwards1211 commented 4 years ago

@bpasero @jrieken @joaomoreno @mjbvz @isidorn @alexdima @sandy081 @Tyriar @aeschli @roblourens What will it take for this to get fixed? Is there something we can do? Would it be worth my time to try to make a PR either to run VSCodeVim in a separate extension host, or enable "type" command extensions to run in the main process (the only solution I think will guarantee acceptable performance in the long run)?

I'm happy to try to dig in and implement a solution, but I'll have to know what kinds of core architectural changes would be approved so I don't spend my time in vain (because fixing this will take core architectural changes).

It's really disappointing to see new features being added when catastrophic technical debt performance issues like this languish. If you're not a Vim user, you probably don't realize how awful it is. I usually even have to disable the builtin TypeScript and JavaScript langauge features extension to get acceptable keyboard responsiveness. At this point, my own workflow with VSCode extensions I've contributed to the ecosystem is the only thing keeping me hanging on to VSCode. But I'll have to jump ship and start contributing to another IDE ecosystem if this issue doesn't get fixed.

alexdima commented 4 years ago

@jedwards1211 Can you please profile the extension host the next time you notice typing is unresponsive for you? This can be easily done by opening F1 > Developer: Show Running Extensions and then clicking the Start Profile action. Once the profile is stopped, the UI should show next to each activated extension how much time it consumed. This should help to clarify who is the culprit for the high latency that you are experiencing.

pdf commented 4 years ago

@alexdima fixing extensions one at a time, forever, is not a reasonable answer to this problem. Text input clearly needs special handling - whilst it would be nice for non-interactive extensions to also do the right thing, they do not need to be on the critical path, however text extensions do.

jedwards1211 commented 4 years ago

@alexdima Come on, man, we want a change to the architecture that allows VSCodeVim to be as responsive as the rest of the UI even if another extension is pegging the extension host. Mouse clicks don't get slowed down when this is happening, nor does builtin keyboard input, so we just won't accept any excuses for why VSCodeVim shouldn't be able to be as responsive as builtin keyboard input. Other IDEs don't have this problem.

alexdima commented 4 years ago

We can look into having another extension host just for vim.

But please try to think also about the following statements:

I consider the extension host to be an integral and critical part of VS Code. I think the VS Code experience with an unresponsive extension host is horrible and should be used only in emergency cases, like saving dirty files and restarting VS Code.

All of those statements above and possibly hundreds more are equally valid if we don't actively try to identify and improve poor performing extensions. I don't see any harm in my asking to profile and make an issue or a PR to a poor performing extension. It is something you can do, involves little VS Code expertise and can scale independently of VS Code. If you manage to find a real performance problem in a popular extension and get that filed and possibly fixed with a PR, you will have improved things for everyone else that uses that extension, independent of the VS Code core. Independent if those people use vim or not, all of them that are using that extension will have more idle time on their CPUs, meaning more free cores that can then be made available to the renderer process, to the GPU process, or their favourite language server process.

We can look into having a special process dedicated to vim, but, please, realise that is partially a bandaid. Those poor performing extensions will still eat up 1 core on your machine per window you have opened, will still make hundreds of other things that depend on the extension host slower, and will eventually clog your CPU and eat your battery for no good reason.

Please, you can help right now by profiling and identifying problems. Doing that is not exclusive with having a special process dedicated to vim.

asvetliakov commented 4 years ago

I think the main problem is the treating vim extensions as "ordinary extensions", however for VIM users these are core editor part, not extension, not even different than built-in keyboard typing. Consider if one day built-in vscode keyboard input would become unresponsive - what would you do? Fix it ASAP, probably even outside of ordinary release schedule.

VIM users are not second class passengers, however this long-term unresolved situation makes me thinks so.

pdf commented 4 years ago

Whilst the sentiment is sensible, of the posited scenarios, only the image editor is analogous to text input, and still less important IMO. As I said, it's desirable for non-interactive extensions to behave, but it's truly a bandaid requiring continual effort to rely on users profiling/patching every poorly performing extension as it's released/updated. Any solution that relies on every single extension being written optimally is destined to fail.

Maybe a separate extension host for type extensions is not the ideal solution, but if solving the problems of the extension host for the general case is (understandably) too hard for the foreseeable future, an interim solution would be greatly appreciated to make the editor experience bearable. Even if other extensions are performing "pretty well", input lag of even minor significance is quite disconcerting.

jedwards1211 commented 4 years ago

@alexdima

realise that is partially a bandaid

Honestly, I wish I could make everyone who doesn't use Vi experience this when typing. If you had this experience daily, you would move heaven and earth to fix it. It's just the worst. It kills braincells.

I'll see if I can profile what's causing problems, but we see fixing poorly performing extensions as a bandaid. Your examples really demonstrate the need for each extension to run in a separate extension host process. I mean even the TypeScript extension blocks Vim for about a second when bringing up the suggestions for aws-sdk (I had to disable automatic suggestions). Is there even a way to fix things that are bound to be expensive like that? Are extensions able to do everything in a Worker?

jedwards1211 commented 4 years ago

To be clear, I'm asking what I can do to help solve this problem permanently. As @pdf has mentioned, debugging poorly performing extensions is not a permanent solution because new performance issues with extensions can pop up at any time.

What you said about improving poorly performing extensions is correct and makes sense, but in this circumstance it seems like pushback to us. The only thing we want to hear from the core team right now is "okay, we see that this situation is miserable, we agree that it's unacceptable and we're going to make the changes necessary to solve this problem permanently." Right now it seems like being a thorn in your side is my only hope of agitating for real change here.

jedwards1211 commented 4 years ago

And clearly I need to repeat that

Other IDEs don't have problems with slow Vi input

kika commented 4 years ago

@alexdima It's worth mentioning that @DanTup puts every effort into optimizing his extension to workaround this bug. And users of the dartcode extension come here only when all the efforts were deemed futile. Me and other vim users submitted him profiles many times over and he did everything possible and then some (short of rewriting vscode) to help us.

jedwards1211 commented 4 years ago

@alexdima

We can look into having another extension host just for vim

Okay, is the core team going to commit to working on this? Or accept a PR for it if I'm able to learn the codebase enough to make one?

jedwards1211 commented 4 years ago

@alexdima

one which I had to defend quite sternly to more experienced folks coming in with an Eclipse background (where everything is single proc, albeit with multiple threads, but oftentimes stuff ended up on the UI thread...)

Well guess what, Eclipse is the IDE I have been most satisfied with, including with the Vrapper extension, it is lightyears more responsive than VSCodeVim. In the past five years or so I haven't noticed any issues with UI lag in Eclipse.

I definitely agree that expensive tasks shouldn't be running on the UI thread by any means. But Vim isn't one of those tasks.

I guarantee you the VSCodeVim experience will suck forever unless it's eventually moved into its own process or into the main process (the idea of every keystroke and buffer interaction having to go over IPC is disgusting enough on its own).

DanTup commented 4 years ago

I do agree that giving Vim its own host specifically isn't an ideal fix. It would prevent someone else coming along and making a very similar extension that performs the same. I think any fix should affect all extensions equally.

However I also agree that having fixing this in every extension is also not a feasible fix. How does an extension author know how long it's reasonable to block for? What do we do when we have to do a lot of work (for example, completion lists these days can be huge when you add in people wanting completions for unimported symbols and the need to supply fill edits for this up-front because we can't supply these asynchronously). The amount of time is also very dependant on the speed of a users machine and the number of extensions trying to run code at the same time. I've already tried to litter yeilds throughout my completion code, but it didn't really solve the issue (it also makes things overall slower if it's done too much or is unnecessary).

There was a question above about Workers that I'm not sure was answered (https://nodejs.org/docs/latest-v12.x/api/worker_threads.html ?). Is it possible/feasible to have extensions run in their own workers without needing separate processes?

pdf commented 4 years ago

I do agree that giving Vim its own host specifically isn't an ideal fix.

Whilst this is the terminology that's been thrown around I suspect if going this route, rather than doing this explicitly, you'd move any extensions that implement type onto a separate host.

tamuratak commented 4 years ago

worker_threads API for extensions tracked in #88386.

DanTup commented 4 years ago

I was thinking more of VS Code doing this automatically (to eliminate this kind of issue) than have extensions do it (even if only for the type-handling extensions). Assuming it could work, wouldn't that be less effort than spawning additional extension hosts?

univerz commented 4 years ago

there is another old & sad issue about input latency #27378

jedwards1211 commented 4 years ago

I just realized, wouldn't it be better for the extension host to automatically report long-running extension tasks than having humans do it every time?

jedwards1211 commented 4 years ago

@jrieken @joaomoreno seeing that you have thumbs up on @alexdima's comment, you have to understand that it comes across as pushing back against our requests for real change. When are we going to have real change in type command performance? Do you care about this?

DanTup commented 4 years ago

wouldn't it be better for the extension host to automatically report long-running extension tasks than having humans do it every time?

VS Code already detects when an extension blocks the thread for long enough (3s?) and gives a warning. I don't think it could really be done for shorter periods (as would be required to catch those affecting typing) because there's such variance across user machines that it'd be triggered all the time.

Knowing what extension is blocking is only half the problem. I can easily give some examples of my own extension causing this issue for Vim users. The issue is - what do I do? The users want completion lists with thousands of items (symbols that aren't yet imported, that will automatically add the imports) and the time it takes to deserialise the results from the language server, map them into VS Code classes (including building edits for things like the import statements) then having VS Code serialise them to go over to the editor can easily add up (esp on slow/busy machines) to longer than the delay people would want to see keypresses delayed by.

Add that the user may have lots of extensions all running code. It doesn't seem realistic to me to say "all of the users extensions combined must run in a single shared thread but never block combined for more than some very small number of ms".

tamuratak commented 4 years ago

workerpool greatly works for LaTeX Workshop. Parsing LaTeX files for suggestions, resizing PNG files for hovers with jimp, and rendering PDF files to SVG files for hovers with PDF.js are asynchronously executed in separate processes. It is straightforward to use. You can try it.

jedwards1211 commented 4 years ago

As popular as Vim is, you'd think this would be considered more important.

image

https://insights.stackoverflow.com/survey/2018/#technology-most-popular-development-environments

jedwards1211 commented 4 years ago

@dantup I'm going to ask the author of VSCodeVim if he's willing to ask all of its users to come upvote this issue...maybe ask users of your extension to upvote this as well?

It's really sad how apathetic the maintainers are about this.

MBetters commented 4 years ago

+1

nhjk commented 4 years ago

I started having this issue after I added the flutter/dart googleapis package, probably due to the amount of exports it has. Typing stutters for up to second while it tries to autocomplete, it was disrupting enough that I was considering dropping vscode. I set "dart.autoImportCompletions": false and it seems to have resolved it. However, this is not ideal and I'd really like to see this issue get resolved. I think I saw this same issue with typescript in combination with large packages such as the aws sdk.

jedwards1211 commented 4 years ago

I can confirm I have the same issue with the AWS SDK and built-in TS/JS extension.

sdykae commented 4 years ago

For some reason neovim extension and simplevim don't have this problem.

jedwards1211 commented 4 years ago

@sdyalor both of those extensions unbind from the type command during insert mode to avoid this bug while typing in text. However commands outside of insert mode can still get delayed just as much as ever.

jedwards1211 commented 4 years ago

@alexdima lately I've seen the builtin TS/JS extensions block Vim for several seconds when I'm in the middle of typing type parameter annotations and it tries to rebuild the outline (I see an intermittent progress bar in the outline view while it's not taking keyboard input).

@tamuratak @jrieken @rebornix @joaomoreno I know you all want me to open a separate issues for other extensions' performance problems like this but please understand from my perspective that seems like it would perpetuate my suffering more in the long term. I think the only hope of seeing a permanent solution where type commands run in a separate thread is to report other extensions' perf causing problems here.

askfiy commented 2 years ago

This problem two years ago is still unresolved. For me, vim is irreplaceable, but vscode is replaceable. If fleet is released, I think I will give up vscode, which is really frustrating.

alexdima commented 2 years ago

In our latest Insiders Build, we have added a new experimental setting that would allow to execute the vim extension in a dedicated extension host process.

Configure the following in your settings.json and then reload window or restart:

"extensions.experimental.affinity": {
    "vscodevim.vim": 1,
    "asvetliakov.vscode-neovim": 1
},

You can double check that vim is loaded in a separate process using F1 > Developer: Show Running Extensions:

image
macintacos commented 2 years ago

@alexdima can we expect this to come in the next release? I'm hesitant to move to Insiders from stable (just as a general rule of thumb) but am willing to move over if it'll be "experimental" for a while before officially released.

UPDATE: today's 1.66 release of VSCode has apparently added this setting (although it isn't noted in the changelog, as far as I can tell). I'll take it!

askfiy commented 2 years ago

This feature came too late.

It's available now and it's great.

bronson commented 2 years ago

This setting has a GUI now.

image

Add a line for vscodevim.vim and restart. Totally worth it. For me, it makes a huge difference!

RaviPabari commented 1 year ago

This setting has a GUI now.

image

Add a line for vscodevim.vim and restart. Totally worth it. For me, it makes a huge difference!

Thank you so much! This worked for me

roszell commented 1 year ago

This fix is working great for me. Both the vim and neovim extensions were randomly very slow/laggy prior to setting this.

ronakg commented 1 year ago

Does affinity work on https://vscode.dev/ as well?

alexdima commented 1 year ago

Does affinity work on https://vscode.dev/ as well?

Thanks to https://github.com/microsoft/vscode/pull/164150 , it will also work on vscode.dev with the next stable release.

faustaleonardo commented 1 year ago

Thank you. This makes a huge difference 👍

yamakoud commented 1 year ago

I'm using M1 mac. I used to use the amd version of vscode, but since I started using the arm version, it's better.

arm 64 version is here https://code.visualstudio.com/docs/?dv=osx

JakubKoralewski commented 1 year ago

Windows x86_64. A lot better, but still not at IntelliJ VIM / terminal VIM speed. (C# omnisharp file analysis running in background on 350 files w/ .NET host ~50% CPU usage)