Search code examples
c#performancebluetoothbinarywriter32feet

Why is sending data with BinaryWriter so much faster using Write(Byte[]) instead of using foreach and Write(Byte)?


I'm using C# and 32feet (version 3.5) to send blocks of 250 bytes over bluetooth (SPP) to an embedded device I'm currently writing firmware for.

I'm setting up my connection with the following code:

var client = new BluetoothClient();
client.Encrypt = true;
client.Connect(bluetoothAddress, ServiceClassId);
NetworkStream stream = client.GetStream();
var writer = new BinaryWriter(stream);

I got some problem with very low throughput and each block took about 100 ms to be transferred with the following code:

public void SendData(List<byte> data)
{
    try
    {
        foreach (byte d in data)
        {
            writer.Write(d);
        }
        writer.Flush();
    }
    catch (Exception)
    {
        // The connection was lost
        ConnectionCleanup();
    }
}

After changing the code block above to the code below each block is transmitted within 4 ms.

try
{
    writer.Write(data.ToArray());
    writer.Flush();
}
catch (Exception)
{
    // The connection was lost
    ConnectionCleanup();
}

I'm struggling to understand how this "simple" code change can have such large impact on the throughput. Can anyone help me explain what's going on? I guess it has something to do with the underlying mechanism of 32feet?

I've changed the code back and forth and the result is the same every time. The transmitted data is also the same.

I've also connected to the device directly from Windows and then opened the COM-port in Realterm to send the same data. In this case I get similar throughput as using writer.Write(data.ToArray()).

I'm using the Microsoft Bluetooth Stack.


Solution

  • Take a look at the reference source for BinaryWriter, Write(byte) calls the underlying stream's WriteByte(byte), while Write(byte[]) calls Write(byte[], int, int). Looking further, we see that NetworkStream does not override the virtual method WriteByte so the base implementation is used:

    // Writes one byte from the stream by calling Write(byte[], int, int).
    // This implementation does not perform well because it allocates a new
    // byte[] each time you call it, and should be overridden by any 
    // subclass that maintains an internal buffer.  Then, it can help perf
    // significantly for people who are writing one byte at a time.
    public virtual void WriteByte(byte value)
    {
        byte[] oneByteArray = new byte[1];
        oneByteArray[0] = value;
        Write(oneByteArray, 0, 1);
    }
    

    Also, NetworkStream has no internal buffers, it simply passes the Write calls to the underlying Socket. You are making 250 network calls in the first case and 1 in the second, so the reason for the performance difference should be obvious.