-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and motivation
Currently, SocketAddress has public int Size and byte this[int index] accessors; if you need the data, accessing the underlying contents (without IVTA) means using your own buffer of the .Size, and then looping to copy the data out. This is inefficient and unnecessary.
Only read access is being considered here; it is not seen as necessary (by me, at least) to support write access (which would make the internal hash optimization awkward), nor to support storage between calls - hence ReadOnlySpan<byte> seems ideal.
Originating context: Kestrel (aspnetcore) investigation into socket overheads and perf/scalability, looking into alternative underlying implementations.
API Proposal
Add a span accessor (note: naming is hard!):
public ReadOnlySpan<byte> Span => new ReadOnlySpan<byte>(Buffer, 0, InternalSize);This enforces all the same characteristics of the existing get accessor, but allows the contents to be accessed much more efficiently.
API Usage
SocketAddress addr = ...
var bytes = addr.Span;
// use bytes(contrast to)
SocketAddr addr = ...
var bytes = /* your choice of temporary buffer here, using addr.Size */
for (int i = 0 ; i < addr.Size; i++)
{
bytes[i] = addr[i];
}
// use bytesAlternative Designs
- could be
AsSpan()method instead of property getter? - could use
CopyTo(Span<byte>), but that still forces a copy, rather than in-place direct usage
Risks
A caller could use unsafe / Unsafe to access the underlying buffer; changing the contents is already allowed (by the indexer set accessor), so that isn't by itself an additional risk - however, this would not trigger the hash-code optimization path. This, though, seems a case of "do stupid things, win stupid prizes", i.e. "don't do that". As soon as unsafe / Unsafe were used, all guarantees were gone.
Using a simple span accessor dictates that the memory has a contiguous representation; this restricts future flexibility for refactoring, although this seems pretty unlikely. But maybe a bool TryGetSpan(out ReadOnlySpan<byte>) (or TryGetBuffer, naming) would be more future-proof there? so: some hypothetical future implementation can at least say "nope" without faulting?
Not a risk, but additionally, Buffer and InternalSize are currently internal rather than private - I wonder how much code that uses those could be updated to use this new API, and perhaps benefit from bounds-check elision (a loop that uses InternalSize will not benefit, but a loop over a right-sized span: will).