statianzo / Fleck

C# Websocket Implementation
MIT License
2.31k stars 585 forks source link

Sending an ArraySegment object #314

Open kuzux opened 3 years ago

kuzux commented 3 years ago

If we are populating a very large buffer and want to send that via a websocket; we need that buffer to be of "right" size. To doi that, we need to either create a new byte[] for each message, or truncate the large buffer to the message size. If we do the truncration thing for a byte[] object, what we get is an ArraySegment<byte> object, not a new byte[] object. So, it should be possible to pass an ArraySegment instead of just a byte array for binary data.

AppVeyorBot commented 3 years ago

:white_check_mark: Build Fleck 0.0.61-ci completed (commit https://github.com/statianzo/Fleck/commit/ba7ddb68ff by @kuzux)

AppVeyorBot commented 3 years ago

:white_check_mark: Build Fleck 0.0.61-ci completed (commit https://github.com/statianzo/Fleck/commit/ba7ddb68ff by @kuzux)

statianzo commented 3 years ago

Could you provide context beyond the new code?

kuzux commented 3 years ago

Here's the case where that would be useful:

In a codebase using Fleck, I was creating a buffer, filling the buffer in native code and then sending that buffer. Something like

byte[] buf  = new byte[MaxSize];
while(some_condition) {
    // ...
    int bufsize = fillBuffer(buf); // native method called with p/invoke
    byte[] sendBuf = new byte[bufsize];
    buf.CopyTo(sendBuf, 0);
    conn.Send(sendBuf);
    // ...
}

So, I want to send just a portion of that buffer (the part filled by the native code). To extract that part as a new byte array, the only "solution" I was able to find (to creating a smaller byte array from a larger one) was allocating a new buffer and copying the data to that (Causes latency spikes when working with buffers of large data).

The way to do that is, I believe, using an ArraySegment<T> object, which is just a separate "view" to the existing underlying array. So instead of creating a new byte array and copying existing data to it, I can create an ArraySegment object and pass that to the socket

statianzo commented 3 years ago

And you’ve found a measurable improvement with this code?

I ask because it still writes to a memory stream that gets written to an array. To avoid allocation of the intermediate array, I believe you’d need to write directly to the socket.

kuzux commented 3 years ago

About measurable improvement: Well there was measurable (in fact noticeable) improvement when I eliminated other allocate & copy operations elsewhere in the code. So I decided to go on an allocation-hunting spree, essentially. This was the low-hanging fruit.

I know about the memory stream allocation, but eliminating that as well would probably take way too much modification in the source, which I wasn't comfortable doing. After all, having 2 allocation & copy operations made a significant improvement over having 3. Having 1 would still be better than having 2 of them (Even if it's not as good as having 0 :D)

AppVeyorBot commented 3 years ago

:white_check_mark: Build Fleck 0.0.62-ci completed (commit https://github.com/statianzo/Fleck/commit/4d644b9f28 by @kuzux)

AppVeyorBot commented 3 years ago

:white_check_mark: Build Fleck 0.0.62-ci completed (commit https://github.com/statianzo/Fleck/commit/4d644b9f28 by @kuzux)

dougbenham commented 2 years ago

+1 for this, definitely a needed feature

dougbenham commented 2 years ago

@kuzux Don't forget to add the Send method to the IWebSocketConnection!