【问题标题】:Serial port performance - VB.NET vs C++ & Boost串口性能 - VB.NET vs C++ & Boost
【发布时间】:2018-02-26 07:39:04
【问题描述】:

我无法理解为什么我的应用程序需要这么长时间才能通过 Windows 10 上的串行端口与设备进行通信。我编写了两个小型测试应用程序来尝试看看是什么让它如此缓慢。这是它们的代码:

''VB.NET code    
Imports System.IO.Ports

Module Module1

    Sub Main()
        Dim port As New SerialPort("COM3", 921600, Parity.None, 8, 1)

        port.Open()

        port.DtrEnable = True
        port.RtsEnable = True

        Dim profiler As New Stopwatch

        profiler.Start()

        For i As Integer = 1 To 100
            port.Write("1PA?" & vbCrLf)
            port.ReadLine()
            port.Write("TB" & vbCrLf)
            port.ReadLine()
        Next

        profiler.Stop()

        Console.WriteLine("Average: " & profiler.ElapsedMilliseconds / 100 & "ms")

        Console.ReadKey()
    End Sub

End Module

还有:

//C++ code
#include <iostream>
#include <string>

#include "boost/asio/io_service.hpp"
#include "boost/asio/serial_port.hpp"
#include "boost/asio/read_until.hpp"
#include "boost/asio/write.hpp"
#include "boost/asio/streambuf.hpp"
#include "boost/asio/buffer.hpp"
#include "boost/thread.hpp"
#include "boost/ref.hpp"
#include "boost/lexical_cast.hpp"

using boost::asio::io_service;
using boost::asio::serial_port;
using boost::asio::streambuf;

size_t read_until(serial_port& port, streambuf& buf, const std::string& delim)
{
    return boost::asio::read_until(port, buf, delim);
}

void complete(const boost::system::error_code& error, std::size_t bytes_transferred)
{
    if (error)
        std::cout << "Error\n";
}

int main()
{
    std::cout << "Starting...\n";

    io_service io;
    serial_port port(io, "COM3");
    streambuf buf(1000);

    boost::posix_time::ptime t0 = boost::posix_time::microsec_clock::local_time();

    port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
    port.set_option(boost::asio::serial_port_base::parity());
    port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port::flow_control::hardware));
    port.set_option(boost::asio::serial_port_base::baud_rate(921600));
    port.set_option(boost::asio::serial_port_base::character_size(8));


    for (int i = 0; i < 100; ++i)
    {
        boost::asio::write(port, boost::asio::buffer("1PA?\r\n", 6));

        read_until(port, buf, "\r\n");
        buf.consume(buf.size());

        boost::asio::write(port, boost::asio::buffer("TB\r\n", 4));

        read_until(port, buf, "\r\n");
        buf.consume(buf.size());
    }

    boost::posix_time::ptime tE = boost::posix_time::microsec_clock::local_time();

    std::cout << (tE-t0).total_milliseconds() << '\n';

    std::cin.get();
}

问题在于 VB.NET 代码报告平均每次循环迭代约 6 毫秒(即每个写入/读取对 3 毫秒),而 C++ 代码每次迭代需要超过 60 毫秒。

项目的其余部分是用 C++ 编写的,所以我需要改进该代码,不能简单地使用另一个代码。目前,我发现最快的方法是通过 TCP/IP 与将 TCP/IP 路由到串行端口的 VB.NET 应用程序进行通信。奇怪的是,尽管涉及额外的步骤,但它的速度是直接 C++ 实现的两倍多。

我有什么遗漏的吗,也许是 C++ 实现中的一个设置?我已经尝试过所有的流量控制选项,不同的缓冲区大小,...

【问题讨论】:

  • 您是否使用优化的构建进行测试?如果没有,请这样做。
  • @JesperJuhl:这不太可能在这里有所作为。
  • 你试过分析它吗?
  • 您将boost 用于I/O,它首先强调可移植性,然后才是吞吐量。使用 OS API 的响应能力会好得多。
  • @Ben Voigt 这仍然是“如何分析”的第一步。

标签: c++ vb.net performance serial-port boost-asio


【解决方案1】:

您看到的写入/读取/写入/读取序列的 60 毫秒与计划传输的典型默认值非常吻合。这是 FTDI(非常流行的 USB 串行端口芯片组)的端口配置

如果串口初始化代码没有明确设置超时,你得到的是每 16ms 的预定传输。

如果您改为调用SetCommTimeouts,您可以安排 USB 设备在每次串行 RX 线上出现间隙时转发接收到的数据缓冲区。为此,请将ReadIntervalTimeout 设置为仅几个字节的传输时间。在 921 kbaud 时,每个字节需要 10-11 微秒,因此ReadIntervalTimeout 的 1 毫秒的最低可能超时对应于大约 92 个字节的间隙。

但是,由于各种 USB 设备附带的驱动程序的实现质量不同,可能会遇到不支持字符间超时的硬件的设备。在这种情况下,最好禁用ReadIntervalTimeoutReadTotalTimeoutMultiplier,而只使用ReadTotalTimeoutConstant

使用这些超时配置中的任何一个,通过 USB 的数据传输将更及时,您的代码将取得进展。

但是,每次接收的 USB 延迟仍约为 3 毫秒(RX 线路忙时为 1 毫秒,空闲时为 1 毫秒以触发超时,另外 1 毫秒以等待下一个 USB 时隙)。为了优于每 3 毫秒 1 条消息,您需要通过使用一些滑动窗口方案替换停止等待协议来进行管道化,以便多条消息在传输中。

【讨论】:

  • 非常感谢您的详细解答和解释。查看代码,似乎 boost 已经完成了您的建议(请参阅here)。但是,我通过执行您链接的documentation page 的备注部分中提到的操作来实现它,即将ReadIntervalTimeoutReadTotalTimeoutMultiplier 设置为MAXDWORD,将ReadTotalTimeoutConstant 设置为1
  • @Mathe172:嗯,我知道我的 C++ 代码不会遭受如此长的延迟。但是,我不使用 I/O 完成端口,我使用完成回调(例如ReadFileEx)。另一方面,尽管 boost 的代码在这里、那里和任何地方都显示 'iocp',但 read_until 调用 do_read 似乎使用的是 GetOverlappedResult 而不是完成端口。也许您可以捕获串行端口上 I/O 操作的跟踪(包括时间)? Sysinternals 工具,无论是端口监视器还是进程监视器,都应该非常擅长。然后我们将查看读取是否被拆分等。
  • @Mathe172:我看到你在我做进一步研究时编辑了你的评论。对于不支持字符间超时,仅支持总超时的 USB 设备,这种超时组合将是一种有效的解决方法。因为那时字符间超时将在软件中模拟,并且仅在计划的缓冲区传输发生后处理。我会将这种可能性添加到我的答案中。
  • 再次感谢您的解释!
猜你喜欢
  • 1970-01-01
  • 2016-04-09
  • 2012-03-17
  • 1970-01-01
  • 2014-10-21
  • 2013-12-04
  • 2012-01-16
  • 2012-07-15
  • 2011-11-10
相关资源
最近更新 更多