4 notes &
Squeezing some extra performance out of System.Net.Sockets.Socket.Send
I had never noticed it before but System.Net.Sockets.Socket.Send contains a couple of pretty useful overloads:
Send(IList<ArraySegment<Byte>>)
Looking at those methods you are probably thinking: “Wow, thats disgusting, why do that when you could just use a big array? A list of arrays? That sounds incredibly slow.” You are probably imagining the implementation looks something like this:
foreach (ArraySegment seg in segments) {
Send (seg);
}
But what this method actually does is take advantage of the underlying OS sendmsg () function on Unix and WSASend on windows. These functions take advantage of a feature known as scatter/gather or vectored IO. sendmsg () takes a message structure that represents a list of buffers, just like the Socket.Send method. To me, the term vectored IO describes this process a little better.
Using vectored IO has a number of benefits. Obviously making one big write instead of lots of little writes is faster, but the real benefit for .NET developers is it means we don’t have to put all our data in a contiguous array. This means a lot less buffering and moving around of data is needed.
At a higher level, we might change our code from this:
MemoryStream stream = new MemoryStream ();
public void Buffer (byte [] data)
{
stream.Write (data);
}
to something like this:
List<ArraySegment<byte» buf = new List<ArraySegment<byte» ();
public void Buffer (byte [] data)
{
buf.Append (data);
}
Obviously the second case is going to be a lot more efficient with big chunks of data and the first case will probably be better with little chunks. So you might want to mix the two, depending on your application.
The other nice thing about vectored IO is its handled at a kernel level, so a lot of runtime overhead is cut out of the picture.
The one gotcha here is when that send doesn’t guarantee to send all of your data. In a single array scenario this is pretty easy to handle. You just update your index by the amount of data that has been sent. When you have a list of bytes, things get a little trickier. Its nothing special, but you can see how I’ve done this in the UpdateSegments method found here: IOStream.cs.
You can read more about Vectored I/O here: