Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

app using "excessive" CPU



  • Hi all -

    I have a small app whose display looks like this:
    discovery.PNG

    The model grows to about 50 columns (most of which are hidden) and 20 rows -- certainly not huge. And in the first few seconds, the model rarely gets updated.

    Multiple users have complained about it using too much CPU (about 11% on my i3 when seemingly idle).

    Given that this is running on Windows, I can't use the profiler built into Creator. Any tips for trying to determine where all this CPU is going?

    Thanks...



  • I would assume that for some reason it is receiving a lot of events or a thread is busy doing "something". Possibly a combination. Is there a way to tap into and print out the events the app is receiving while idle?



  • Good assumption - I'm getting thousands of events per second, all like this:

    15466 "15:46:02" QEvent::Paint
    15467 "15:46:02" QEvent::UpdateRequest
    15468 "15:46:02" QEvent::Paint
    15469 "15:46:02" QEvent::UpdateRequest
    15470 "15:46:02" QEvent::Paint
    15471 "15:46:02" QEvent::UpdateRequest
    15472 "15:46:02" QEvent::Paint
    15473 "15:46:02" QEvent::UpdateRequest
    15474 "15:46:02" QEvent::Paint
    15475 "15:46:02" QEvent::UpdateRequest
    

    But what could be causing this? My model isn't changing (certainly not that quickly) and there's no user input, or resizing going on. The app, from the user's perspective, is truly idle.



  • This is guessing time. I am good, but not that good. But I will hazard a guess. Something is "wiggling" and it most likely caused by something that controls layout. Something adjusts, something else adjusts it back.

    I do most of my work in QML and when this happens it is usually detected as a binding loop. I am unsure if there is anything like that in QWidgets.



  • You can start the debugger and after start randomly click on pause.
    Since the program will be inside Paint or UpdateRequest with a high probability you will get there quickly and then you can follow the call-stack to see what is causing it.



  • @gde23 that's a clever idea, and I tried it. Unfortunately, whenever I pause the debugger, the call stack only displays routines pertinent to handling the pause:pause.PNG
    Great idea, though.


  • Lifetime Qt Champion

    Hi,

    Are the columns automatically resized to content ?



  • @SGaist not explicitly. I do this:

        qhv->setSectionResizeMode(QHeaderView::Stretch);
    

    But disabling that line doesn't reduce CPU noticably.



  • @mzimmers
    Sit down, grab a coffee & a pack of cigs. Start binary-chopping-commenting-out half your code at a time. It can't be that hard to get to the point where these update requests do not happen... :) For example, first get rid of all hidden columns. Then reduce to fewer columns than the width of the window. With 2 rows. Then no rows and no columns --- does it still happen then?!

    What are you using which gives you those events printed out?



  • @JonB OK, I'll belly up to removing the hidden rows (I was hoping to avoid that).

    I've already sized my widget so that everything is fully displayed.

    I even disconnected the signal that tells the model to update. My tableView is completely empty. And I'm still getting the CPU usage.

    Here's what I use to show me the events:

    bool KeyPress::eventFilter(QObject *obj, QEvent *ev)
    {
        bool rc = false;
        QEvent::Type type;
        static int count = 0;
    
        type = ev->type();
        qDebug() << count++ << QTime::currentTime().toString() << type;
    
        rc = QObject::eventFilter(obj, ev);
        return rc;
    }
    

    It may be noteworthy that disabling this filter entirely doesn't help.


  • Lifetime Qt Champion

    Hi
    Could you not add a paintEvent handler where you simply call base class but
    then add a break point and that way maybe see how its triggered ?



  • @mzimmers said in app using "excessive" CPU:

    My tableView is completely empty. And I'm still getting the CPU usage.

    Which is the sort of thing which is interesting, so you don't want to waste time looking at resizes. Nonetheless, the events indicate widget updating?

    I had forgotten about your KeyPress and its eventFilter. Get rid of it completely from your code! You don't want to be messing about with your key press stuff from your other thread. Now what's the speed? It ought be still slow, but you never know.

    Then get rid of all your own "event filters". Reintroduce only one at the very top level to debug monitor whatever you need to.



  • @mrjj I can do something like this:

    void Widget::paintEvent(QPaintEvent *event)
    {
        QWidget::paintEvent(event);
    }
    

    But I don't know what kind of meaningful information I can extract from the event argument. (According to the docs, paintEvent() isn't a slot.)



  • @JonB I've gotten rid of all filters, all hidden columns, and have disconnect model updates after I create two rows. I also disabled stretch mode, and expanding policy. Still using 11% CPU.

    This might be a good time to point out that I "stole" the code for this app from another project, which doesn't have this problem. For the life of me, I can't see what I added that causes this.



  • @mzimmers
    Try not running your application, or switch the machine off ;-)

    Given that this is running on Windows, I can't use the profiler built into Creator

    Does your MSVC/MinGW compiler come with a usable profiler, nothing to do with Qt?

    In your paintEvent() override @mrjj had in mind for you to try a debugger breakpoint there and look at the stack trace. You will doubtless need to perhaps put a delay/count on the breakpoint, or set it while the app is in the middle of running. I don't actually know whether paint events will show anything interesting on the stack.

    You can use the top-level eventFilter() to examine most of what's going on, if you have to :( Debug every event's type to file/debug output window for a couple of seconds into your "idle". Search the output, you're probably interested in what you see just before the updating starts, or as it goes along.



  • @JonB using MinGW, and I don't think it has a profiler. I could look at 3rd party products, but it's probably easier just to have a co-worker build this on Linux, and then run the profiler. (Hopefully the problem will occur on Linux!)

    Here's the stack trace you mentioned:
    stack.PNG
    Maybe you can see something useful in it; I can't.



  • @mzimmers
    An expert may have something to say about the traceback. All I know is it would be nice to know which the QWidget is. Wait, this code is yours for Widget? So which of your widgets is it? If you break more than once, is it always the same widget?

    I can see QCoreApplication::sendSpontaneousEvent(). You sure you're not "wiggling"? :)

    (Hopefully the problem will occur on Linux!)

    Nope... ;-)



  • @JonB yes Widget is my oh-so-creative name for my main QWidget class, which is the only QWidget the app uses (unless you push one of the buttons). So yeah, it's always the same Widget.

    Someone else mentioned wiggling, but I'm not sure I know what it means. The problem occurs even when the app loses focus, though.

    And, if you're confident this won't happen on Linux, then maybe the problem isn't in my code space...



  • @mzimmers said in app using "excessive" CPU:

    And, if you're confident this won't happen on Linux, then maybe the problem isn't in my code space...

    Noooo, I put a wink --> ;-) <--
    I absolutely do not know whether it will repro under Linux, if you're lucky it will, I just meant sod's law it won't!


  • Lifetime Qt Champion

    @mzimmers
    Hi
    That stack trace is for

    1. starting app
      2: add break point
      3: let it loose focus to see the issue

    and not just set at startup so we ssee the first paint when it becomes visible?
    Just asking to be sure. Not seeing anything special besides maybe the sendSpontaneousEvent



  • @mrjj I'm not sure I follow you, but I modified my routine:

    void Widget::paintEvent(QPaintEvent *event)
    {
        static int count = 0;
        if (event->spontaneous())
        {
            //qDebug() << "spontaneous event" << count++;
        }
        else
        {
            QWidget::paintEvent(event);
        }
    }
    

    I put a breakpoint on QWidget::paintEvent...and it never, EVER hits. (Also, with this change, CPU usage remains the same.)

    This just gets weirder and weirder.



  • I am sorry this is such a struggle. I don't have anything more to add except a sarcastic example of wiggling:
    https://media.giphy.com/media/xT9KVjBI3W2283URdm/source.mp4


  • Lifetime Qt Champion

    @fcarney
    hehe that the sorts you want to debug with a flame thrower...


  • Lifetime Qt Champion

    @mzimmers

    well i just asked if it was not as in first run stack trace.
    but i think you are doing as i think reading your last post.

    Its very odd. Indeed.
    Does
    //qDebug() << "spontaneous event" << count++;
    trigger alow when testing then ?



  • @mrjj I just realized something -- from the docs:

    bool QEvent::spontaneous() const
    Returns true if the event originated outside the application (a system event); otherwise returns false.
    
    The return value of this function is not defined for paint events.
    

    So, I think this exercise was a waste of time.


  • Lifetime Qt Champion

    @mzimmers

    Well i find it very odd you see many paint events for the widget (with event filter) but
    its not constantly Hitting the break point in paintEvent. ??
    Or did i misunderstood something ?



  • @mrjj you understand it perfectly, and "odd" is a mild term for it.

    I seriously don't know what to look at here. I'm afraid that the profiler we run tomorrow won't show anything in my code space.


  • Lifetime Qt Champion

    @mzimmers
    Do you have any timers or threads ?
    Something scanning for those devices you show ?

    Also, just for test. a plain normal GUI project with say a ListWidget on it does not show this
    cpu usage, right?

    It must somehow be related to your code ?



  • @mzimmers said in app using "excessive" CPU:

    I'm afraid that the profiler we run tomorrow won't show anything in my code space.

    Always with the negative waves https://www.youtube.com/watch?v=KuStsFW4EmQ Have a little faith, baby!



  • Well, my associate hasn't yet built the app on Linux, but I discovered the source (if not the cause) of the problem: my logo.

    I have a small (201x59 pixel) PNG in the upper left of my widget. When I remove it from my .qrc file, the CPU usage disappears.

    Any ideas on this one?



  • @mzimmers said in app using "excessive" CPU:

    my logo

    Wow! That is all I can say.
    🍿



  • I suspect it has something to do with this line:

    painter.drawPixmap(rect(), pixmap()->scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    

    I don't need to scale it; what do I replace the call to scaled() with?


  • Lifetime Qt Champion

    @mzimmers said in app using "excessive" CPU:

    painter.drawPixmap(rect(), pixmap()->scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));

    painter.drawPixmap(rect(), pixmap());
    actaully scale it to rect so @Bonnie version is correct.
    

    to draw it at original size.

    or you can simply scale it once outside paintEvent and reuse the scaled version.



  • To not scale at all, should not use a rect as parameter (unless the rect size is the same as the pixmap size).

    painter.drawPixmap(0, 0, pixmap());
    

    (0, 0) is the draw position.


  • Moderators

    @mzimmers great you found it! But paintEvent itself should not be called that much regularly! I suspect your logo is being resized (wiggles) all the time!



  • @mzimmers
    Ah ha! We have been looking for where you "wiggle", I suspect @J-Hilk has found it!?



  • Thank you all for the replies. I tried mrjj's/Bonnie's suggestions, and they didn't change the CPU usage.

    But really, the question (IMO) is WHY is my logo being wiggled? I have matched the dimensions of the .png file to the QWidget (a QLabel) that displays it. I've set the QLabel's size to fixed. What could be causing this flood of paint events?

    The technique I'm using is to promote the QLabel to a class I've created (LogoLabel). Here's the entirety of its paint event:

    void LogoLabel::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event)
        QPainter painter(this);
        const QString filename(":/logos/CYBERDATA_IP_ENDPOINT_CO_small.png");
    
        bool rc;
    
        rc = m_pixmap.load(filename);
        if (rc)
        {
            setPixmap(m_pixmap);
            Qt::KeepAspectRatio, Qt::SmoothTransformation));
            painter.drawPixmap(0, 0, *pixmap());
        }
    }
    


  • Hi,

    You could reimplement "reseizeEvent" and do the Pixmap scale there. On "paintEvent", just paint it or just set the pixmap to the Label and it will paint it.



  • @mzimmers Why are you doing the loading in the paintEvent???

    void LogoLabel::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event)
         //m_pixmap should already be loaded
        if (!m_pixmap.isNull())
        {
            QPainter painter(this);
            painter.drawPixmap(0, 0, m_pixmap);
        }
    }
    

    I remembered you had a post said you write your label class because you want the QLabel to scale smoother.
    Now, if you do not need to scale, you only need to load the pixmap once after it is created.

    label.setPixmap(QPixmap(":/logos/CYBERDATA_IP_ENDPOINT_CO_small.png"));
    

    If you still need to scale, I think you should scale it in the resizeEvent.

    void LogoLabel::resizeEvent(QResize*event)
    {
        QLabel::resizeEvent(event);
        if(QPixmap *pix = pixmap())
            m_pixmap = pix->scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    }
    

    Actually, I don't think you need to subclass QLabel...Subclassing QWidget is enough if you reimplement all these events...



  • @Bonnie you nailed it. I moved the load to the c'tor, and everything works much better now.

    A couple of notes:

    1. I turned on my event filter, and it's no longer spewing zillions of events, so I guess my keyPress idea would have worked (if Windows wasn't so lame).
    2. I tried making my painter a member variable and initializing it in the c'tor, but that didn't work. Any idea why?

    Thanks to everyone who looked at this.


Log in to reply