简体   繁体   English

C#哪个更快:SerialPort.Write(byte [],int,int)或SerialPort.BaseStream.WriteByte()?

[英]C# Which is faster: SerialPort.Write(byte[], int, int) or SerialPort.BaseStream.WriteByte()?

I am working some code that controls a device via serial port (over usb). 我正在工作一些通过串行端口(通过USB)控制设备的代码。 This application will need to be able to run in the background, yet push a fair amount of data continuously over the port, so I need the data writing code to be reasonably fast. 该应用程序将需要能够在后台运行,但是需要通过端口连续推送大量数据,因此我需要数据写入代码要相当快。 My code pushes its data in large batches ("frames") multiple times per second. 我的代码每秒多次大批量推送其数据(“帧”)。 Currently, I have a Queue that is used to generate the commands that need to be sent for the current frame. 目前,我有一个队列,用于生成当前帧需要发送的命令。 Would it be faster to just iterate through the Queue and push my commands one at a time using SerialPort.BaseStream.WriteByte(byte), or use the Queue to build up a byte array and send it all at once using SerialPort.Write(byte[], int, int)? 使用SerialPort.BaseStream.WriteByte(byte)一次遍历队列并一次推送我的命令会更快,还是使用Queue建立一个字节数组并使用SerialPort.Write(byte)一次全部发送,会更快吗? [],int,int)?

Some example code, if my description is confusing: Which is faster, this: 一些示例代码,如果我的描述令人困惑:哪个更快,这:

   public void PushData(List<KeyValuePair<Address, byte>> data) {
        Queue<DataPushAction> actions = BuildActionQueue(data);
        foreach(var item in actions) {
            port.BaseStream.WriteByte(item.Value);
        }
    }

or this: 或这个:

    public void PushData(List<KeyValuePair<Address, byte>> data) {
        Queue<DataPushAction> actions = BuildActionQueue(data);
        byte[] buffer = actions.Select(x => x.Value).ToArray();
        port.Write(buffer, 0, buffer.Length);
    }

Update 更新资料

Upon closer inspection of the source code, it seems that both methods are the same (WriteByte just uses a temporary array with one element and is otherwise the same as Write). 在仔细检查源代码后,似乎这两种方法是相同的(WriteByte仅使用具有一个元素的临时数组,否则与Write相同)。 However, this doesn't actually answer the question, just rephrases it: Is it faster to write many small arrays of bytes or one large one? 但是,这实际上并不能回答问题,只是改写一下:写许多小的字节数组还是一个大的字节数组更快吗?

By modifying the code in Rion's answer below, I was able to test this and got some surprising results. 通过修改下面Rion的答案中的代码,我能够对其进行测试并获得一些令人惊讶的结果。 I used the following code (modified from Rion, thanks): 我使用了以下代码(谢谢Rion的修改):

    class Program {
    static void Main(string[] args) {
        // Create a stopwatch for performance testing
        var stopwatch = new Stopwatch();
        // Test content
        var data = GetTestingBytes();

        var ports = SerialPort.GetPortNames();
        using (SerialPort port = new SerialPort(ports[0], 115200, Parity.None, 8, StopBits.One)) {
            port.Open();

            // Looping Test
            stopwatch.Start();
            foreach (var item in data) {
                port.BaseStream.WriteByte(item);
            }
            stopwatch.Stop();
            Console.WriteLine($"Loop Test: {stopwatch.Elapsed}");

            stopwatch.Start();
            port.Write(data, 0, data.Length);
            stopwatch.Stop();
            Console.WriteLine($"All At Once Test: {stopwatch.Elapsed}");
        }

        Console.Read();
    }

    static byte[] GetTestingBytes() {
        var str = String.Join(",", Enumerable.Range(0, 1000).Select(x => Guid.NewGuid()).ToArray());
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
}

The results were extremely surprising; 结果非常令人惊讶; using the method that takes an array of bytes took almost exactly twice as long at 12.5818728 seconds compared to 6.2935748 seconds when just calling WriteByte repeatedly. 使用需要字节数组的方法所花费的时间几乎恰好是12.5818728秒的两倍,而重复调用WriteByte的时间为6.2935748秒。 This was the opposite result than I was expecting. 这是与我预期相反的结果。 Either way, I wasn't expecting one method to be twice as fast as the other! 无论哪种方式,我都没想到一种方法会比另一种方法快两倍!

If anyone can figure out why this is the case, I would love to know! 如果有人能弄清楚为什么会这样,我很想知道!

Based on a quick glance at the source , it looks like the SerialPort.Write() method actually just points to the Write() method of the underlying stream anyways : 快速浏览一下源代码 ,看起来SerialPort.Write()方法实际上实际上只是指向基础流的Write()方法:

public void Write(byte[] buffer, int offset, int count)
{
            if (!IsOpen)
                throw new InvalidOperationException(SR.GetString(SR.Port_not_open));
            if (buffer==null)
                throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Buffer));
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (buffer.Length - offset < count)
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (buffer.Length == 0) return;

            internalSerialStream.Write(buffer, offset, count, writeTimeout);
}

If I had to wager a guess, I would assume that the difference between the two performance wise might be negligible. 如果我不得不猜测,我会假设这两个绩效方面的差异可以忽略不计。 I suppose if you had some test data, you could create a StopWatch to actually time the differences between both approaches. 我想如果您有一些测试数据,则可以创建一个StopWatch来实际计时两种方法之间的差异。

Update with Performance Tests 更新性能测试

I didn't test this using SerialPort objects, but instead opted for basic MemoryStream ones as the real heart of the question seemed to be if writing bytes in a loop is more performant than writing them using a byte[] . 我没有使用SerialPort对象进行测试,而是选择了基本的MemoryStream对象,因为问题的真正核心似乎在于,在循环中写入字节是否比使用byte[]写入性能更高。

For test data, I simply generated 1000 random Guid objects and concatenated them into a string : 对于测试数据,我只生成了1000个随机的Guid对象并将它们连接成一个字符串:

static byte[] GetTestingBytes()
{
        var str = String.Join(",", Enumerable.Range(0, 1000).Select(x => Guid.NewGuid()).ToArray());
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
}

As far as the tests themselves go : 就测试本身而言:

// Create a stopwatch for performance testing
var stopwatch = new Stopwatch();
 // Test content
var data = GetTestingBytes();

// Looping Test
using (var loop = new MemoryStream())
{
      stopwatch.Start();
      foreach (var item in data)
      {
            loop.WriteByte(item);
      }
      stopwatch.Stop();
      Console.WriteLine($"Loop Test: {stopwatch.Elapsed}");
}
// Buffered Test
using (var buffer = new MemoryStream())
{
      stopwatch.Start();
      buffer.Write(data, 0, data.Length);
      stopwatch.Stop();
      Console.WriteLine($"Buffer Test: {stopwatch.Elapsed}");
}

After running the tests a few times, the averages after 1000 tests broke down as follows : 进行几次测试后,经过1000次测试的平均值如下:

  LOOP: 00:00:00.0976584
BUFFER: 00:00:00.0976629

So the looping approach, at least in the context of using MemoryStream objects, appears to be the winner. 因此,至少在使用MemoryStream对象的情况下,循环方法似乎是赢家。

You can see the entire testing code here if you want to run it yourself. 如果您想自己运行它,则可以在此处查看整个测试代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM