This evening I made an optimization to KRatingPainter (a class in libnepomuk in kdelibs) that I just have to blog about. I was using the KRatingPainter class to paint one of those 5-star rating dealies onto the item delegate of a listview. When I maximized the window, scrolling this list view became very slow. I broke out callgrind for a debugging session and scrolled down the list using the arrow key whilst my other hand assisted me in eating a jelly doughnut whilst I waited for the (now considerably slower thanks to callgrind) view to scroll enough to get sufficient data from callgrind.
Looking at callgrind’s output, the culprit was obvious. The paint() function of KRatingPainter was 20% of all computing time that had been spent during the program’s run. More specifically, the KIconEffect constructions going on in paint() were the culprit.
So I delved into the source to figure out why so many KIconEffects were being constructed during paint(). It turned out that KRatingPainter was using the KIconEffect class to apply enabled/disabled/hover effects to the stars it painted, but was creating a new KIconEffect each time it wanted to paint(). It ended up creating 2 KIconEffect objects every time paint() was called. KIconEffects are quite expensive to construct, as they have to read configuration files each time they are made. (The above picture illustrates the expensiveness nicely.)
But, as it turns out, KIconLoader provides a shared KIconEffect object that all KDE apps can use (unless they want a custom one for some reason). Using a pointer to this shared KIconEffect object rather than creating 2 per paint resulted in a speedup of near 500x.
Whereas before KRatingPainter had consumed 20% of all computing time spent, the second time around KRatingPainter painting took only 0.04% of all computing; meaning that, if it wanted too, it could have done 500 times more paints in the same amount of time it took to paint the first time around. Notice too from the picture that KIconEffect::KIconEffect (the constructor) was only called once after the fix, whereas it was called 400-something times without the fix. So take this as a lesson: always use the shared KIconEffect unless it is absolutely necessary not to.
This was just to epic a win not to blog about.