3

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.

Markus
  • 767
  • 2
  • 7
  • 19
  • 1
    This seems oddly self explanatory. You're flushing one byte at a time to the underlying stream which is obviously less efficient than writing all 250 at once. – Timeout Mar 31 '16 at 19:15
  • Take a look at the [reference source](http://referencesource.microsoft.com/#mscorlib/system/io/binarywriter.cs,cf806b417abe1a35), `Write(byte)` calls the underlying stream's `Write(byte[], int, int)` (NetworkStream does not override `WriteByte`) with a buffer of a single byte, while `Write(byte[])` calls the same method but just passes along the whole array. – Mike Zboray Mar 31 '16 at 19:22
  • Your question is "why is calling a method 250 times slower than calling it once?" Those method calls are not free! Neither is all the gear in the foreach that iterates over every element. The one that is slower is doing 250 times more incidental work than the one that is faster. – Eric Lippert Mar 31 '16 at 19:32
  • Well, it's quite obvious the way you guys put it. I actually thought that the stream had an internal buffer that the incoming data were just appended to and then this buffer was flushed to the underlying mechanism when calling Flush(). That way the two alternatives should, in my world, perform nearly the same (except for the extra overhead introduced by the iteration and the method calls but that shouldn't make up for 96 ms). – Markus Mar 31 '16 at 19:40
  • Thank you @mikez for showing me the reference source. Never seen it before and it made me realize that there are no internal buffers that works the way I assumed. If you put your comment as an answer I'll accept it as you pointed me in the right direction and provided enough information to help me with my question. – Markus Mar 31 '16 at 19:44

1 Answers1

3

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.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122