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
Fixing CA2014 warnings, and removing suppressions #17982
Conversation
src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs
Outdated
Show resolved
Hide resolved
3cc72af
to
f13d8a6
Compare
Pinging the original reviewers, to get a few more eyes on the PR. |
{ | ||
var endOfLine = Environment.NewLine.AsSpan(); | ||
var endOfLineLength = endOfLine.Length; | ||
Span<char> outBufferLine = stackalloc char[outBuffer.Length + endOfLineLength]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we could allocate ~16KB (MaxBufferSize) in stack. That it is not good. I suggest to output the tail before the condition if the tail >= MaxStackAlloc ( MaxStackAlloc = 512).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov
I think what you are suggesting is that we should further split the outBuffer
into 2 pieces -> MaxBufferSize - 512b and 512b, such that the span that is stacalloc'ed is 512 bytes.
(assuming that the size of the outBuffer
is MaxBufferSize
, in the worst case)
The first MaxBufferSize - 512b piece will need additional memory. This will also end up going on the stack since we are using spans. This route does not seem any better than allocating the entire MaxBufferSize.
Also, this will mean that we would now need to make two WriteConsole
calls to finish writing the contents of the outBuffer, rather than 1.
I don't think the memory allocation issue is significant enough to justify adding the perf of a whole other IO operation.
Let me know if I understood that right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean:
internal static void WriteConsole(ConsoleHandle consoleHandle, ReadOnlySpan<char> output, bool newLine)
{
Dbg.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
Dbg.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");
const char MaxStackAlloc = 512;
Span<char> buffer = stackalloc char[MaxStackAlloc];
ReadOnlySpan<char> outBuffer;
// Native WriteConsole doesn't support output buffer longer than 64K.
// We need to chop the output string if it is too long.
// This records the chopping position in output string
int cursor = 0;
// this is 64K/4 - 1 to account for possible width of each character.
const int MaxBufferSize = 16383;
while (cursor <= output.Length - MaxBufferSize)
{
outBuffer = output.Slice(cursor, MaxBufferSize);
cursor += MaxBufferSize;
WriteConsole(consoleHandle, outBuffer);
}
if (cursor == output.Length)
{
if (newLine)
{
WriteConsole(consoleHandle, Environment.NewLine);
}
return;
}
outBuffer = output.Slice(cursor);
if (!newLine)
{
WriteConsole(consoleHandle, outBuffer);
return;
}
if (outBuffer.Length < MaxStackAlloc - 1)
{
// We expect it is a hot path.
var endOfLine = Environment.NewLine.AsSpan();
var endOfLineLength = endOfLine.Length;
buffer = buffer.Slice(0, outBuffer.Length + endOfLineLength); }
outBuffer.CopyTo(buffer);
endOfLine.CopyTo(buffer.Slice(buffer.Length - endOfLineLength));
WriteConsole(consoleHandle, buffer);
}
else
{
WriteConsole(consoleHandle, outBuffer);
WriteConsole(consoleHandle, Environment.NewLine);
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I understand your concern here. But the while (cursor + MaxBufferSize < output.Length)
comes from the existing if (cursor + MaxBufferSize < output.Length)
, so it's not a problem introduced by this cleanup change. So, I'm fine merging this PR as is, and submit a new PR to address your concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@daxian-dbw Agree.
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@creative-cloud Thanks for your contribution! |
PR Summary
Fixes #13531 - No longer allocating memory in loops. Removed related suppress warnings.
Moved out the stackalloc statements for 2/3 cases, and removed the loop entirely for the other.
PR Context
Copying context over from the issue -
PR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
or[ WIP ]
to the beginning of the title (theWIP
bot will keep its status check atPending
while the prefix is present) and remove the prefix when the PR is ready.(which runs in a different PS Host).