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

Release unneeded memory more eagerly from conhost #10738

Merged
1 commit merged into from
Jul 21, 2021

Conversation

lhecker
Copy link
Member

@lhecker lhecker commented Jul 20, 2021

The _CONSOLE_API_MSG buffer is resized to cover an entire message.
Later on any UTF-8 data is cached in a separate temporary
buffer inside til::u8state to prevent lone surrogate pairs.

Both cases are problematic as neither buffer is freed after the read
has finished. Passing a 100MB buffer to conhost once will thus cause it
to continue using ~220MB of physical memory until the conhost process exits.

This change releases unneeded memory as soon as the requested buffer
size has halved. In practice this means that once a command has returned
all buffers will shrink, as the shell commonly sends very small messages.

PR Checklist

Validation Steps Performed

  • Buffers aren't reallocated during printing ✔️
  • Buffers shrink after printing finished ✔️

@lhecker lhecker requested review from DHowett and miniksa July 20, 2021 21:59
@ghost ghost added Area-Server Down in the muck of API call servicing, interprocess communication, eventing, etc. Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Conhost For issues in the Console codebase labels Jul 20, 2021
_outputBuffer.resize(cbWriteSize);

// 0 it out.
std::fill(_outputBuffer.begin(), _outputBuffer.end(), (BYTE)0);
std::fill_n(_outputBuffer.data(), _outputBuffer.size(), BYTE(0));
Copy link
Member Author

@lhecker lhecker Jul 20, 2021

Choose a reason for hiding this comment

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

This was sort of a programming error that I fixed on the side.
<algorithm> doesn't work properly with non-STL containers, so it's important that we pass it raw pointers if we can. Otherwise it can't figure out what to do with it and sets each byte to 0 one by one in this case for instance.
This one will now properly compile to a single call to memset().

Copy link
Member

Choose a reason for hiding this comment

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

Excellent. Thanks!

@lhecker lhecker force-pushed the dev/lhecker/server-memory-limit branch from 524effd to e023dfa Compare July 20, 2021 22:04
Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

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

I'm comfortable with these as quick point-in-time fixes!

@DHowett
Copy link
Member

DHowett commented Jul 20, 2021

@msftbot make sure @miniksa signs off

@ghost ghost added the AutoMerge Marked for automatic merge by the bot when requirements are met label Jul 20, 2021
@ghost
Copy link

ghost commented Jul 20, 2021

Hello @DHowett!

Because you've given me some instructions on how to help merge this pull request, I'll be modifying my merge approach. Here's how I understand your requirements for merging this pull request:

  • I'll only merge this pull request if it's approved by @miniksa

If this doesn't seem right to you, you can tell me to cancel these instructions and use the auto-merge policy that has been configured for this repository. Try telling me "forget everything I just told you".

Copy link
Member

@miniksa miniksa left a comment

Choose a reason for hiding this comment

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

I'm fine with this, but I'm stunned shrink_to_fit actually works. Probably boost::small_vector actually implements it. I think actually shrinking is optional in the standard.

@ghost ghost merged commit 8779249 into main Jul 21, 2021
@ghost ghost deleted the dev/lhecker/server-memory-limit branch July 21, 2021 06:00
@german-one
Copy link
Contributor

@lhecker Once that I worked on the code I was facing a huge performance drop caused by shrink_to_fit and I explicitely removed it in an early stage.
#4093 (comment)
I recall that I was quite surprised because it seems shrink_to_fit doesn't just flag the unused memory space as available for the OS. Based on the influence I've observed I'm of the opinion that a new buffer whith updated size is allocated, the used content is moved to the new memory space, and after that the old buffer is released.

I'm absolutely aware that your latest update is not the same as what I originally did. I called shrink_to_fit literally after each and every conversion. However, I just want to highlight that it may affect the performance. (And it was all about performance back then 😄)

@lhecker
Copy link
Member Author

lhecker commented Jul 21, 2021

@german-one Yeah in this case specifically we're only dropping the buffer after calling .clear().
That way no memory copy occurs and instead the buffer is dropped entirely.
I wrote a tool to test output performance at various sizes and as long as buffers are <16 KiB, or of a consistent size, no reallocation, nor performance drop occurs.
This reminds me though that I should add a test to my tool, which measures performance for randomly sized buffers.

Rosefield pushed a commit to Rosefield/terminal that referenced this pull request Jul 22, 2021
The `_CONSOLE_API_MSG` buffer is resized to cover an entire message.
Later on any UTF-8 data is cached in a separate temporary
buffer inside `til::u8state` to prevent lone surrogate pairs.

Both cases are problematic as neither buffer is freed after the read
has finished. Passing a 100MB buffer to conhost once will thus cause it
to continue using ~220MB of physical memory until the conhost process exits.

This change releases unneeded memory as soon as the requested buffer
size has halved. In practice this means that once a command has returned
all buffers will shrink, as the shell commonly sends very small messages.

## PR Checklist
* [x] Closes microsoft#10731
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

* Buffers aren't reallocated during printing ✔️
* Buffers shrink after printing finished ✔️
DHowett pushed a commit that referenced this pull request Aug 25, 2021
The `_CONSOLE_API_MSG` buffer is resized to cover an entire message.
Later on any UTF-8 data is cached in a separate temporary
buffer inside `til::u8state` to prevent lone surrogate pairs.

Both cases are problematic as neither buffer is freed after the read
has finished. Passing a 100MB buffer to conhost once will thus cause it
to continue using ~220MB of physical memory until the conhost process exits.

This change releases unneeded memory as soon as the requested buffer
size has halved. In practice this means that once a command has returned
all buffers will shrink, as the shell commonly sends very small messages.

## PR Checklist
* [x] Closes #10731
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

* Buffers aren't reallocated during printing ✔️
* Buffers shrink after printing finished ✔️
@ghost
Copy link

ghost commented Aug 31, 2021

🎉Windows Terminal Preview v1.10.2383.0 has been released which incorporates this pull request.:tada:

Handy links:

@ghost
Copy link

ghost commented Aug 31, 2021

🎉Windows Terminal Preview v1.11.2421.0 has been released which incorporates this pull request.:tada:

Handy links:

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Server Down in the muck of API call servicing, interprocess communication, eventing, etc. AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Conhost For issues in the Console codebase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

conhost memory usage stays elevated after writing large buffers
4 participants