Zasobożerny program, komunikacja rs232

0

Witam, mam problem z zasobożernym programem. Podczas odczytywania danych zużywa sporo mocy obliczeniowej procesora. Testowałem na słabszym sprzęcie, Core 2 Duo( T5500) czyli 2x1.66, 2gb ram ddr2, ale pamięć operacyjna nie jest tu chyba istotna, sytem windows xp 32bit. Przeważnie zużycie procesora podczas odczytu to 60%, wydaje mi się to trochę za dużo, jak na tak prosty program. Gdzie popełniłem błąd? Program ma mieć określoną przestrzeń na przechowywanie danych (wątek główny rysuje je na wykresie), potem dane są usuwane i dokładane nowe na wektorze.
Program napisany w Qt, kompilator MiniGW32.

void PortReader::readData()
{
    if(status==true){
        r_data = port->readAll();
        port_buffor_size = r_data.size();
        qDebug() << "port_buffor_size: " << port_buffor_size;
        for(int j=0;port_buffor_size > j;j++){
            odebraneDane->push_back(r_data.data()[j]);
            qDebug() << "r_data.data()[j]" << r_data.data()[j];
            }
        }
        //Jeśli wielkość wektora przekroczy maksimum setSizeArry
        if(odebraneDane->size() > portArrySize){
            while(odebraneDane->size() > portArrySize){
                odebraneDane->removeFirst(); //usuń pierwszy element wektora
            }

        emit odebranoDane();
    }
    else{
        qDebug() << "Przychodzą dane, ale czekam.";
    }
}

portreader.cpp - pastebin
portreader.h -pastebin

1
  1. Ile wynosi portArrySize?
  2. Czy ta metoda jest wywoływana w pętli? Pokaż tą pętlę.
1

Kiedy wywoływane jest readData? Czy to jest slot podłączony do właściwego sygnału?
Czy prawidłowo reagujesz na niepełne dane?
Czy w ogóle profilowałeś program, skąd pewność, że w tym miejscu program ci wisi?

Jak podasz dokładny opis jakie dane przychodzą ci z portu (lub przynajmniej kilka przykładów) i co chcesz z nimi zrobić to będziemy w stanie sensowniej pomóc.

1

@Vitling:
Widzę, że Qt, więc trochę podpowiem:

  • używasz qDebug, i to takiego, który bardzo dużo printuje (zakładam, że ta funkcja i tak jest wołana w pętli),
  • skoro masz qDebug(), to kompilowałeś w trybie debug z symbolami.
    Spróbuj zmienić kompilację na releasową. Powinno to trochę odchudzić zarówno program jak i jego zapotrzebowanie na zasoby.

+bonus @_13th_Dragon: możesz użyć ++j zamiast j++ ;)

1

Wróżąc z magicznej kuli ma być tak:

// pola kalasy
QByteArray odebraneDane;

// inicjalizacja
rs232 = new QSerialPort(name, this);
connect(rs232, &QSerialPort::readyRead,
        this, &PortReader::readData);
rs232.open(QIODevice::ReadWrite);

void PortReader::readData()
{
    auto r_data = port->readAll();
    qDebug() << "port_buffor_size: " << r_data.size();
    odebraneDane.append(r_data);
    
    if (odebraneDane.size() > portArrySize) {
            odebraneDane.remove(0, odebraneDane.size() - portArrySize);
    }
    emit odebranoDane();
}

Bez opisu jakie dane przetwarzasz więcej się nie da.

0

@Juhas: portArrySize podczas testu wynosił 500, przy wartości 1000 było podobne zużycie. Metoda jest wywoływana podczas odebrania danych z portu.

connect(port,&QSerialPort::readyRead,this,&PortReader::readData);

@MarekR22 Wydaje mi się, że metoda readData jest podłączona do właściwego sygnału, jest tak jak napisałeś wyżej. Przed zapisem do odebraneDane sprawdzam jaka jest wielkość bufora w r_data, o to chodzi z niepełnymi danymi?. Pliki klasy są w linkach pod kodem, wklejone na pastebin dla wygody. Klasa jest oddzielnym wątkiem, ma za zadanie odbierać wartości od 0 do 255 (jest to prosty miernik napięcia zrobiony na Atmega8, robię pomiar ADC i wysyłam do komputera przez rs232). Program zaczyna być zasobożerny w momencie podłączenia sprzętu pod port com. Odłączam, obciążenie znika, po tym wnioskuje, że problem jest w metodzie readData.

 if (odebraneDane.size() > portArrySize) {
            odebraneDane.remove(0, odebraneDane.size() - portArrySize);
    }

Dużo mądrzejsze, bez pętli, tak zrobię, zobaczę efekty.

@xfin Kompilacja była relesowa do testu na innym komputerze, pomyślałem o tym. Na komputerze na którym kompilowałem to różnica spora między debug, a release w zużyciu procka, fakt.

Nie wiem jak to sobie poukładać, jak przechowywać dane, może jakaś kolejka LIFO? Będzie szybciej niż operacja na wektorach? Dopiero się ucze, może STL oferuje coś co świetnie nadaje się do częstego przesuwania danych. odebraneDane są typu QVector<double> bo taki jest wymagany do klasy rysującej wykres. Odłączyłem w ramach testów rysowanie wykresu, a obciążenie jest podobne.

1

Nie wiem, jak działa QVector. Ale pewnie analogicznie do std::vector. A spróbuj zamiast tego dać std::deque. Przy takiej wielkości wektora i tylu wkładanych danych na pewno będzie bardziej optymalne.

1

Jeśli to jest prawdziwy port RS-232 to wątpię byś miał taki zalew danych by faktycznie zużywać znacząco CPU (z racji powolności tego portu).
Pozostaje też pytanie jak używasz PortReader?
Piszesz coś o wątkach. Z tym kodem jest to nie jest to potrzebne. Pytanie jak używasz swojego obiektu PortReader? Jeśli przesunąłeś go na inny watek, to wszelkie wywołania funkcji z innego (głowneg) wątku powinieneś wykonywać tylko i wyłączanie przez wywołanie sygnałów połączonych z odpowiednimi slotami PortReader (Qt wtedy wykonuje wtedy thread hopping).
Jeśli tak nie robisz to możliwe, że masz race condition w wielu miejscach.

Ja widzę 100% "race condition" na *odebraneDane, bo modyfikujesz QVector na jednym wątku a odczytujesz na drugim bez synchronizacji. To może dawać dziwne efekty jak spowolnienie pracy.

Popraw to tak:

// pole w klasie
QByteArray odebraneDane;

// porawiony sygnał
public signals:
    void odebranoDane(const QByteArray &dane);

void PortReader::readData()
{
    auto r_data = port->readAll();
    if (r_data.empty())
          return;

    qDebug() << "port_buffor_size: " << r_data.size();
    odebraneDane.append(r_data);
 
    if (odebraneDane.size() > portArrySize) {
            odebraneDane.remove(0, odebraneDane.size() - portArrySize);
    }
    emit odebranoDane(odebraneDane);
}

Inne rozwiązanie to nie używać wątków, wtedy problem synchronizacji ci zniknie. Naprawdę nie ma takiej potrzeby.

0

@MarekR22: Zrobiłem coś na wzór, jak podałeś wyżej. Czasem coś pokaże na wykresie, ale ogólnie mam wrażenie że główny wątek zalewany jest sygnałami, rysowanie trwa trochę dłużej od pobierania danych, coś czuję, że będę musiał ogarnąć mutexy.

void PortReader::readData()
{
    if(status==true){
        r_data = port->readAll();
        if(r_data.isEmpty()) return;
        emit odebranoDane(r_data);
    }
    else{
        qDebug() << "Przychodzą dane, ale czekam.";
    }
}
void MainWindow::paintPortChart(QByteArray portData){
    portData_size = portData.size();
    qDebug() << "portData_size: " << portData_size;
    for(int i=0;i<portData_size;i++){
        y.push_back(portData.data()[i]);
        qDebug() << portData.data()[i];
    }

    int xSize = y.size();
    if(xSize > xWidth){
        y.remove(0,xSize - xWidth);
    }
    ui->wykres->graph(0)->setData(x,y);
    ui->wykres->replot();

}
0
Vitling napisał(a):

mam wrażenie że główny wątek zalewany jest sygnałami, rysowanie trwa trochę dłużej od pobierania danych, coś czuję, że będę musiał ogarnąć mutexy.

Z tego wynika, że problemem nie jest pobieranie danych.
Czyli żeby wiedzieć coś na pewno musisz użyć prolifera i zmierzyć, gdzie twój program się przytyka.

BTW co to jest ui->wykres? Qwt czy coś z Qt?

1 użytkowników online, w tym zalogowanych: 0, gości: 1