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:
@@ -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 };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user