Skip to content
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

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

Open
DanTup opened this issue Jun 17, 2019 · 52 comments
Assignees
Labels
feature-request Request for new features or functionality perf VIM VIM issue

Comments

@DanTup
Copy link
Contributor

DanTup commented Jun 17, 2019

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
Copy link
Member

Similar issue #65876 . @DanTup thanks for the nice write up.

@rebornix rebornix added perf under-discussion Issue is under discussion for relevance, priority, approach labels Jul 18, 2019
@alexdima
Copy link
Member

alexdima commented Aug 7, 2019

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
Copy link
Contributor Author

DanTup commented Aug 7, 2019

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
Copy link
Member

alexdima commented Aug 7, 2019

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
Copy link
Contributor Author

DanTup commented Aug 7, 2019

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
Copy link
Contributor

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.

@alexdima alexdima added the VIM VIM issue label Aug 26, 2019
@alexdima alexdima added feature-request Request for new features or functionality and removed under-discussion Issue is under discussion for relevance, priority, approach labels Oct 8, 2019
@DanTup
Copy link
Contributor Author

DanTup commented Nov 21, 2019

The Rust analyzer extension also uses this command:

rust-lang/rust-analyzer#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
Copy link
Contributor

jedwards1211 commented Apr 9, 2020

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:

  • Eclipse
  • IntelliJ
  • Visual Studio
  • Qt Creator
  • Sublime
  • WebStorm
  • Atom
  • CodeSandbox

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
Copy link
Contributor

jedwards1211 commented Apr 14, 2020

@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
Copy link
Member

@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
Copy link

pdf commented Apr 14, 2020

@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.

@askfiy
Copy link

askfiy commented Dec 7, 2021

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
Copy link
Member

alexdima commented Mar 25, 2022

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
Copy link

macintacos commented Mar 28, 2022

@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
Copy link

askfiy commented Apr 10, 2022

This feature came too late.

It's available now and it's great.

@bronson
Copy link

bronson commented Apr 30, 2022

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
Copy link

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
Copy link

roszell commented Oct 21, 2022

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

@ronakg
Copy link

ronakg commented Oct 21, 2022

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

@alexdima
Copy link
Member

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

Thanks to #164150 , it will also work on vscode.dev with the next stable release.

@faustaleonardo
Copy link

Thank you. This makes a huge difference 👍

@yamakoud
Copy link

yamakoud commented Jul 16, 2023

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
Copy link

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)

@CleyFaye
Copy link

I stumbled upon this issue while searching for laggy typing and missed input. After running the profiler I found two extension that had >150ms runtime where other had <1ms: vim and Intellicode.

Using the affinity setting I moved vim out of the way (affinity=1), but the lagginess remained. I also moved intellicode (and the intellicode completion extension) in yet another process (affinity=2 for both of them), and the issue disappeared.

Thanks for that. I'm not sure how desirable it could be to auto-adjust this setting when some extension misbehave, but even as a clutch it works well enough.

@victorallume
Copy link

I'm having this issue re-appear after seemingly being fixed a few months ago with the affinity setting. It seems to be an interaction of two or more extensions. If I disable either pylance or vscodevim, it seems to work happily. But with both of them installed, as soon as I start typing something (in insert mode), I get crazy lag and skipped and repeated keystrokes (and sometimes it treats inline blame annotations (via Gitlens) as actual text in the editor). While the madness is happening (and indeed, every single time I start typing something, VSCode starts analyzing some number of files). Restarting VSCode doesn't fix the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request Request for new features or functionality perf VIM VIM issue
Projects
None yet
Development

No branches or pull requests