Serial performance



  • I am using Qt 5.3 on a Beaglebone Black to control my hardware and display results. My hardware is driven by 3 serial ports running at 115200 baud. The Beaglebone is single core.
    The application is a state machine that sends a command packet (5 bytes) to the serial port. To send a packet, we emit a signal with the packet data and a slot in the Comms sends the data. When data is received, the port is read ( data = this->port->readAll(); ) and a signal is emitted for the packet contents to be checked and a flag for command acknowledge or error is set.
    The next state in the application then checks for the correct flag before proceeding. Standard stuff!
    This works OK but it takes a lot of time for the received packet to be detected and flag set (200ms to 500ms or longer) even though on the oscilloscope it can be seen that the hardware responds in 10ms.

    It seems to me that it takes a long time for the events on the serial port to be processed and there are a lot of command to be processed so the application seems slow.

    My question is, would this benefit from moving the serial port interface code and processing to a separate thread?

    Apologies if this has been asked before (please point me to appropriate answers).

    Regards,
    James

    PS. Processing of the received packet doesn't take long, its just verifying the checksum and setting a flag to say the command has been correctly acknowledged.


  • Qt Champions 2016

    @jastmc said in Serial performance:

    My question is, would this benefit from moving the serial port interface code and processing to a separate thread?

    I doubt it, but probably @kuzulis can give you a better answer.



    1. QSP internally uses the QSocketNotifier. If you use the 'pure' asynchronous state machine and so on (without calls of waitFoxxXX() methods), then it is impossible to have a delays as '200ms to 500ms or longer'. Maybe your remote device introduces delays (too long thinks) at making a response on a request.

    2. ( data = this->port->readAll(); ) does not guarantie that you are receive whole response package.

    3. Version 5.3 is too old, try 5.9.2.

    4. Usually, the threads does not required on 115200 bauds, at this speed is soo slow, to influence on the CPU and Qt-event loop.


  • Qt Champions 2016

    @kuzulis, just out of curiosity, what'd be a typical figure for the latency of QSP and to what you'd attribute it? Is the socket notifier the "slowest link" in the chain leading to readyRead()?



  • Thanks for the prompt replies.

    I am not using any blocking calls and the remote device responds in ~10ms, observed on 'scope.

    I was wondering whether the Qt Event loop could be so busy that it was taking so long to get around to processing the serial port signals?

    I did a simple serial test in a very basic application to just send a packet and, when I received 5 characters back, I sent another packet. In that test, Qt took 10 - 15ms to respond to a received packet.

    In my real application, there are a number of graphics screens, one of which is displayed at a time but all they do is advance a progress bar while we send commands to and get responses from the serial port. That should not be enough to introduce such a large delay!

    Can you give me any tips on how to zone in on the actual cause of the delays in order to break it down into steps?

    Regards,
    James



  • @kshegunov said in Serial performance:

    @kuzulis, just out of curiosity, what'd be a typical figure for the latency of QSP and to what you'd attribute it?

    Usually, less than one millisecond (please see below).

    @kshegunov said in Serial performance:

    Is the socket notifier the "slowest link" in the chain leading to readyRead()?

    I'm don't think so.

    @jastmc, could you please make following test on your board: connect Rx && Tx physically and run following code:

    #include <QCoreApplication>
    #include <QSerialPort>
    #include <QElapsedTimer>
    #include <QDebug>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        QElapsedTimer timer;
        QSerialPort uart("/dev/ttyUSB0");
        QCoreApplication::connect(&uart, &QSerialPort::readyRead, [&uart, &timer]() {
            if (uart.bytesAvailable() < 5)
                return;
            qDebug() << "Rx:" << uart.readAll() << "in" << timer.nsecsElapsed() << "ns";
            timer.start();
            uart.write("Hello");
        });
        uart.setBaudRate(115200);
        uart.open(QIODevice::ReadWrite);
        uart.write("Hello");
        return a.exec();
    }
    

    E.g. for me it prints out following:

    Rx: "Hello" in 645445 ns
    Rx: "Hello" in 616017 ns
    Rx: "Hello" in 598339 ns
    Rx: "Hello" in 639166 ns
    Rx: "Hello" in 604685 ns

    I have used ArchLinux x64, UART USB - pl2303, Qt 5.9.2.

    PS: Sometimes, problems comes from the drivers, e.g: QTBUG-48561, also, maybe your driver does not used a DMA and so on. You need to do tests, e.g. with diffrent uarts (e.g. try to plug the USB serial port or something else to your board), or try to update the kernel, because I can't say nothing more. I never used the boards with a single core CPU.

    In my real application, there are a number of graphics screens,

    Maybe it is reason too.



  • @kuzulis

    I had tried this following test:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "comms.h"
    
    #include <QDebug>
    #include <QSerialPort>
    #include <iostream>
    #include <QObject>
    #include <QByteArray>
    #include <QString>
    #include <QElapsedTimer>
    
    int count;
    QByteArray dataout;
    QByteArray datain;
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        timer = new QElapsedTimer();
    
        dataout.append(0x03);
        dataout.append(0x20);
        dataout.append(0x03);
        dataout.append(0x07);
        dataout.append(0x2d);
    
        serial = new QSerialPort(this);
        serial->setPortName("/dev/ttyO4");
        serial->setBaudRate(QSerialPort::Baud115200);
        serial->setDataBits(QSerialPort::Data8);
        serial->setParity(QSerialPort::NoParity);
        serial->setStopBits(QSerialPort::OneStop);
        serial->setFlowControl(QSerialPort::NoFlowControl);
        if (!serial->open(QIODevice::ReadWrite))
        {
            qDebug() << " Failed to open port";
            qApp->quit();
        }
        if (!connect(serial, (&QSerialPort::readyRead), this, (&MainWindow::readData )) )
        {
            qDebug() << " Can't connect ...";
        }
    
        qDebug() << " Starting...";
        count = 0;
    
        serial->write(dataout);
        serial->flush();
    }
    
    void MainWindow::readData()
    {
        QByteArray chars = serial->readAll();
        din.append(chars);
    
        if (din.size() > 4)
        {
            qDebug() << din.toHex() << "in" << timer->nsecsElapsed() << "ns";;
            din.remove(0,5);
    
            if (count++ < 1000)
            {
                serial->write(dataout);
                serial->flush();
            }
    
        }
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    

    The result was

    Starting...
    "0620030730" in 131405492763 ns
    "0620030730" in 131415504304 ns
    "0620030730" in 131417467096 ns
    "0620030730" in 131419417096 ns
    "0620030730" in 131421047638 ns
    

    The initial response take 10ms and the following ones come quicker.

    So I guess I need to look elsewhere.

    Thanks for your help.

    James



  • @jastmc said in Serial performance:

    "0620030730" in 131417467096 ns
    "0620030730" in 131419417096 ns

    Well, ~1.5-2 msec is too long, IMHO. Try it without a gui app, just as console app (e.g. with that code which I posted).

    UPD: Also, don't use flush().


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.