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

Transfer webview resource buffers #139145

Merged
merged 2 commits into from
Feb 3, 2022
Merged

Conversation

mjbvz
Copy link
Contributor

@mjbvz mjbvz commented Dec 15, 2021

Transfer the underlying buffer of a webview resource load to avoid an extra data copy. This improves performance. The perf improvement is small for normal requests, but becomes more signficant the larger the resource becomes

To support this, I needed to expose a new option that forces VSBuffer to use UInt8Array instead of a Node buffer. In my testing, trying to transfer a node Buffer ends up breaking our general ipc buffers and breaks the entire app. Not sure if there's a better way to do this

@mjbvz mjbvz added this to the January 2022 milestone Dec 15, 2021
@mjbvz mjbvz self-assigned this Dec 15, 2021
@@ -14,8 +14,8 @@ let textDecoder: TextDecoder | null;

export class VSBuffer {

static alloc(byteLength: number): VSBuffer {
if (hasBuffer) {
static alloc(byteLength: number, forceUseUint8Array = false): VSBuffer {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposing this low level flag feels wrong, but as I noted in the issue comment I'm not sure if there is a better way to do this

@bpasero bpasero removed their request for review December 15, 2021 06:57
@alexdima
Copy link
Member

alexdima commented Dec 15, 2021

Here's my understanding of what is going on:

  • in VSBuffer.alloc, we use Buffer.allocUnsafe
  • Buffer.allocUnsafe is a lot faster because it uses the nodejs Buffer pool.
  • it looks like all ArrayBuffers that are used in the Buffer pool are marked as untransferrable.

For this particular optimization, instead of adding forceUseUint8Array and having some VSBuffer instances use nodejs' Buffer and some use Uint8Array, I suggest to have an argument forTransferable or a change that is localized to webviewElement.ts which would force using a nodejs Buffer outside of the Buffer pool. From what I understand from the nodejs documentation, Buffer.concat, Buffer.from and Buffer.allocUnsafe might all end up returning a Buffer from the Buffer pool.

Here's a quick, untested attempt of a change that could be localized to webviewElement.ts. (I wasn't sure what the exact steps would be to reproduce the crash you mentioned):

function concatWithoutNodePool(buffers: VSBuffer[]): VSBuffer {
	const totalLength = buffers.reduce((prev, curr) => prev + curr.byteLength, 0);
	const ret = Buffer.alloc(totalLength);
	let offset = 0;
	for (let i = 0, len = buffers.length; i < len; i++) {
		const element = buffers[i];
		ret.set(element.buffer, offset);
		offset += element.byteLength;
	}
	return VSBuffer.wrap(ret);
}

const buffer = VSBuffer.wrap(await streams.consumeStream<Buffer>(stream, chunks => concatWithoutNodePool(chunks)));

I will document these findings on main in the JSDoc for these functions. Bottom line, I personally find it better to have all VSBuffer either all use nodejs Buffers or all use Uint8Array and then a way to explicitly ask for nodejs Buffers outside of the pool or separate methods alloc/slowAlloc, concat/slowConcat.

cc @deepak1556

sourcegraph-bot pushed a commit to sgtest/megarepo that referenced this pull request Dec 15, 2021
…nodejs Buffer pool and thus might not be transferrable (See microsoft/vscode#139145)

Commit: d1886674451a37c05f5abfacf05e702ecac22fb5
alexdima added a commit that referenced this pull request Dec 15, 2021
…uffer pool and thus might not be transferrable (See #139145)
@mjbvz mjbvz modified the milestones: January 2022, February 2022 Jan 28, 2022
@LifeIsStrange
Copy link

LifeIsStrange commented Feb 1, 2022

@alexdima Reading your comment reminded me of the amazing library
https://github.com/Bnaya/objectbuffer
Which uses this state of the art, very efficient allocator:
https://github.com/thi-ng/umbrella/tree/develop/packages/malloc#about

See performance:

In this micro benchmark, allocating 4KB blocks (1024 floats) is ~20x faster than calling new Float32Array(1024).

Therefore I wonder how this web compatible allocator compare to the current VSbuffer.alloc/allocUnsafe.
Using it might improve performance or memory use for VScode workloads so it would be nice to benchmarck it :)
VScode might also consider using objectbuffer for improved ergonomics regarding arraybuffers/web workers.

Fixes #139145

This updates the webview resource loading to use transferables

On desktop, this requires a new way of converting the file stream to a buffer without using the nodejs backing pool
@mjbvz mjbvz force-pushed the dev/mjbvz/transfer-webview-resource branch from 121032d to 5355e6c Compare February 3, 2022 18:37
@mjbvz
Copy link
Contributor Author

mjbvz commented Feb 3, 2022

Thanks @alexdima! I've re-implemented this all in the webview code

@mjbvz mjbvz merged commit 256cc1d into main Feb 3, 2022
@mjbvz mjbvz deleted the dev/mjbvz/transfer-webview-resource branch February 3, 2022 19:06
@github-actions github-actions bot locked and limited conversation to collaborators Mar 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants