1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-05 16:43:52 +01:00

Reduced CPU usage of Audacity when idle.

Before this change, an idle Audacity on a recent Linux laptop uses 6%
of CPU. This is because DrawOverlays() is called every 50ms through a
timer in TrackPanel, which instanciates a wxClientDC. This is quite
expensive and dominates the profiles: creating wxClientDCs is 84% of
the CPU audacity spends while idle, according to callgrind. With this
change, we avoid creating wxClientDCs when it's not necessary.

After this change, and idle Audacity consumes 1.6% of CPU, and most of
the time is spend in gtk/wxwidgets processing events.

Here are the perf stats of an idle Audacity before the change:

        524.425485      task-clock (msec)         #    0.060 CPUs utilized
               825      context-switches          #    0.002 M/sec
                36      cpu-migrations            #    0.069 K/sec
                 0      page-faults               #    0.000 K/sec
     1,198,433,346      cycles                    #    2.285 GHz
     1,243,329,771      instructions              #    1.04  insn per cycle
       308,073,049      branches                  #  587.449 M/sec
         5,801,494      branch-misses             #    1.88% of all branches

       8.808129958 seconds time elapsed

and after:

        149.110455      task-clock (msec)         #    0.016 CPUs utilized
               908      context-switches          #    0.006 M/sec
                32      cpu-migrations            #    0.215 K/sec
                 0      page-faults               #    0.000 K/sec
       313,372,582      cycles                    #    2.102 GHz
       167,401,770      instructions              #    0.53  insn per cycle
        41,857,947      branches                  #  280.718 M/sec
         1,189,566      branch-misses             #    2.84% of all branches

       9.076940003 seconds time elapsed
This commit is contained in:
Raphaël Marinier
2016-07-21 10:48:16 +02:00
parent cf2625a7bd
commit 173652875d
2 changed files with 26 additions and 10 deletions

View File

@@ -37,11 +37,11 @@ void OverlayPanel::ClearOverlays()
mOverlays.clear();
}
void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC)
void OverlayPanel::DrawOverlays(bool repaint_all, wxDC *pDC)
{
size_t n_pairs = mOverlays.size();
std::vector< std::pair<wxRect, bool> > pairs;
std::vector< std::pair<wxRect, bool /*out of date?*/> > pairs;
pairs.reserve(n_pairs);
// Find out the rectangles and outdatedness for each overlay
@@ -53,12 +53,15 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC)
// If not, then whatever is outdated, and whatever will be damaged by
// undrawing.
// By redrawing only what needs it, we avoid flashing things like
// the cursor that are drawn with invert.
if (!repaint) {
// the cursor that are drawn with invert, and also avoid
// unnecessary work.
bool some_overlays_need_repainting = repaint_all;
if (!repaint_all) {
bool done;
do {
done = true;
for (size_t ii = 0; ii < n_pairs; ++ii) {
some_overlays_need_repainting |= pairs[ii].second;
for (size_t jj = ii + 1; jj < n_pairs; ++jj) {
if (pairs[ii].second != pairs[jj].second &&
pairs[ii].first.Intersects(pairs[jj].first)) {
@@ -70,6 +73,14 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC)
} while (!done);
}
if (!some_overlays_need_repainting) {
// This function (OverlayPanel::DrawOverlays()) is called at
// fairly high frequency through a timer in TrackPanel. In case
// there is nothing to do, we exit early because creating the
// wxClientDC below is expensive, at least on Linux.
return;
}
Maybe<wxClientDC> myDC;
auto &dc = pDC ? *pDC : (myDC.create(this), *myDC);
@@ -77,7 +88,7 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC)
bool done = true;
auto it2 = pairs.begin();
for (auto pOverlay : mOverlays) {
if (repaint || it2->second) {
if (repaint_all || it2->second) {
done = false;
pOverlay->Erase(dc, GetBackingDC());
}
@@ -88,7 +99,7 @@ void OverlayPanel::DrawOverlays(bool repaint, wxDC *pDC)
if (!done) {
it2 = pairs.begin();
for (auto pOverlay : mOverlays) {
if (repaint || it2->second) {
if (repaint_all || it2->second) {
// Guarantee a clean state of the dc each pass:
ADCChanger changer{ &dc };