Description
Description
When using a MemoryMappedViewStream
and calling the .Read(Span<byte>)
method on it, we will end up thunking down through System.IO.Stream.Read(Span<byte>)
which utilizes an intermediate byte[]
buffer out of ArrayPool<byte>.Shared
instead of simply block transferring bytes directly from the memory mapped section into the Span that was given.
runtime/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs
Lines 790 to 809 in 1d1bf92
This seems to be because the code in UnmanagedMemoryStream
, which is the base class for MemoryMappedViewStream
is falling back to base.Read(Span<byte>)
in any circumstance where the class type isn't exactly itself.
However, MemoryMappedViewStream
doesn't implement anything else for .Read()
here at all and likely wants to be hitting UnmanagedMemoryStream.ReadCore(Span<byte>)
for this activity so the high-performance unsafe raw memory move is performed without any intermediate allocations or fluff.
I think the error is that it goes straight to base.Read(Span<byte>)
here. However, there's a conundrum. You cannot just bare call Read(Span<byte>)
to make it try to resolve to any overload because then it will probably stack overflow here when it's not overloaded. And there's no real good way to go backwards from Span<byte>
to byte[]
such that we could instead go for the Read(byte[], int, int)
variant.
For now, my workaround is to simply create the MemoryMappedViewStream
and hold onto that so it maintains the lifetime of all the underlying gunk and then make myself a second UnmanagedMemoryStream
by forwarding the relevant parameters out of the MemoryMappedViewStream
directly into the UnmanagedMemoryStream
constructor following what happens in the Initialize call here:
Configuration
I'm targeting .NET 8.0 and my SDK installed is 8.0.411.
Regression?
No.
Data
I can't share this publicly. I ran the profiler on non-public source, but I don't think this is necessary because not allocating or borrowing an array is always cheaper/faster than doing so.
Analysis
I'm pretty sure that UnmanagedMemoryStream.Read(Span<byte>)
needs to be adjusted to less aggressively forward calls down to the base class. I'm just not sure exactly how.