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

Eliminate allocation in StreamAsIStream.Read #6632

Merged
merged 1 commit into from
Jan 29, 2024

Conversation

bgrainger
Copy link
Contributor

@bgrainger bgrainger commented May 31, 2022

Description

The native/managed interop layer will allocate a byte array to marshal a native buffer to the StreamDescriptor.Read delegate. By using Span<byte> and the corresponding overload of Stream.Read, we can read directly into the caller's buffer when CManagedStream::Read calls this delegate.

Sample improvement by running a benchmark to create a BitmapImage with a MemoryStream StreamSource containing a 79KB PNG:

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1706 (21H2)
Intel Core i7-10875H CPU 2.30GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100-preview.4.22252.9
  [Host]     : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT
  Job-FFRIND : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT

Runtime=.NET 6.0  Toolchain=net6.0-windows
Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
Before 3.623 ms 0.0702 ms 0.0656 ms 984.3750 984.3750 984.3750 82 KB
After 3.619 ms 0.1386 ms 0.4088 ms 960.9375 960.9375 960.9375 3 KB

Customer Impact

Unnecessary allocation and memory copy between native/managed buffers.

Regression

No

Testing

Tested in real-world application via incorporation into Faithlife.Wpf.

Risk

Minimal.

Microsoft Reviewers: Open in CodeFlow

The native/managed interop layer will allocate a byte array to marshal a native buffer to the StreamDescriptor.Read delegate. By using Span<byte> and the corresponding overload of Stream.Read, we can read directly into the caller's buffer when CManagedStream::Read calls this delegate.
@bgrainger bgrainger requested a review from a team as a code owner May 31, 2022 01:24
@ghost ghost assigned bgrainger May 31, 2022
@ghost ghost added the PR metadata: Label to tag PRs, to facilitate with triage label May 31, 2022
@ghost ghost requested review from dipeshmsft, singhashish-wpf and SamBent May 31, 2022 01:24
@ghost ghost added the Community Contribution A label for all community Contributions label May 31, 2022
@lindexi
Copy link
Member

lindexi commented May 31, 2022

Although only 4 milliseconds have been reduced, it is important to reduce 79KB of memory allocated.

@bgrainger
Copy link
Contributor Author

Actually only 4 microseconds faster, which is well within the margin of error.

I'm not claiming that this PR is any faster; the primary goal is to eliminate managed allocations (which were previously equal in size to the input size of the image) and GCs.

@miloush
Copy link
Contributor

miloush commented Jun 4, 2022

No regression when the array is bigger than int? Doesn't AsSpan throw with negative numbers?

@bgrainger
Copy link
Contributor Author

There's no regression because the old code cast the length to an int, too: https://github.com/dotnet/wpf/pull/6632/files#diff-43d84464891cb611646041f77d33aaf1c13800873ec0b7ff899f891985500d60L304

@bgrainger
Copy link
Contributor Author

@dipeshmsft Could this PR be considered for the 8.x release series? It feels like a small change that has measurable allocation/GC benefits.

@pchaurasia14
Copy link
Member

@bgrainger - Unfortunately, .NET8 RC candidates are locked in and we won't be able to take this in for .NET8. However, we'll consider this PR for upcoming CTP runs and have it merged asap.

@rchauhan18
Copy link
Contributor

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rchauhan18 rchauhan18 merged commit 2495266 into dotnet:main Jan 29, 2024
8 checks passed
@rchauhan18
Copy link
Contributor

Thanks @bgrainger for the contribution!

@github-actions github-actions bot locked and limited conversation to collaborators Feb 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Community Contribution A label for all community Contributions PR metadata: Label to tag PRs, to facilitate with triage
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

6 participants