【问题标题】:Why is sending data with BinaryWriter so much faster using Write(Byte[]) instead of using foreach and Write(Byte)?为什么使用 Write(Byte[]) 而不是使用 foreach 和 Write(Byte) 使用 BinaryWriter 发送数据要快得多?
【发布时间】:2016-03-31 19:00:35
【问题描述】:

我正在使用 C#32feet(版本 3.5)通过蓝牙 (SPP) 将 250 字节的块发送到我目前正在为其编写固件的嵌入式设备。

我正在使用以下代码设置我的连接:

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

我遇到了一些吞吐量非常低的问题,每个块需要大约 100 毫秒才能传输,使用以下代码:

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

将上面的代码块改为下面的代码后,每个块在4毫秒内传输。

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

我很难理解这种“简单”的代码更改如何对吞吐量产生如此大的影响。谁能帮我解释发生了什么?估计跟32feet的底层机制有关吧?

我来回更改了代码,结果每次都一样。传输的数据也是一样的。

我还直接从 Windows 连接到设备,然后在 Realterm 中打开 COM 端口以发送相同的数据。在这种情况下,我得到的吞吐量与使用 writer.Write(data.ToArray()) 相似。

我正在使用Microsoft Bluetooth Stack

【问题讨论】:

  • 这似乎很奇怪,不言自明。您一次将一个字节刷新到底层流,这显然比一次写入所有 250 个字节效率低。
  • 看一下reference sourceWrite(byte)调用底层流的Write(byte[], int, int)(NetworkStream不会覆盖WriteByte)用一个字节的缓冲区,而Write(byte[])调用相同的方法,但只是传递整个数组。
  • 你的问题是“为什么调用一个方法比调用一次慢 250 倍?”那些方法调用不是免费的!迭代每个元素的 foreach 中的所有设备也不是。速度较慢的人做的附带工作是速度较快的人的 250 倍。
  • 嗯,你们说的很明显。我实际上认为流有一个内部缓冲区,传入的数据只是附加到,然后在调用 Flush() 时这个缓冲区被刷新到底层机制。这样,在我的世界中,这两个替代方案的性能应该几乎相同(除了迭代和方法调用引入的额外开销,但这不应该弥补 96 毫秒)。
  • 感谢@mikez 向我展示了参考来源。以前从未见过它,它让我意识到没有内部缓冲区可以按我假设的方式工作。如果您将您的评论作为答案,我会接受它,因为您为我指明了正确的方向并提供了足够的信息来帮助我解决我的问题。

标签: c# performance bluetooth binarywriter 32feet


【解决方案1】:

看看BinaryWriterreference sourceWrite(byte)调用底层流的WriteByte(byte),而Write(byte[])调用Write(byte[], int, int)。进一步看,我们看到 NetworkStream 没有覆盖虚拟方法 WriteByte,因此使用了base implementation

// 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);
}

另外,NetworkStream 没有内部缓冲区,它只是将写入调用传递给底层Socket。您在第一种情况下进行了 250 次网络调用,在第二种情况下进行了 1 次,因此性能差异的原因应该很明显。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-25
    • 2015-01-23
    • 2020-07-11
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    相关资源
    最近更新 更多