[tor-commits] [nyx/master] Reduce graph panel cpu usage
atagar at torproject.org
atagar at torproject.org
Sat Sep 16 22:37:17 UTC 2017
commit c02be3ee32b4d8cba8a9f4eee5cc97b0fd27bdef
Author: Damian Johnson <atagar at torproject.org>
Date: Sat Sep 16 13:36:01 2017 -0700
Reduce graph panel cpu usage
Our graph panel updates and redraws itself every second, making it a
non-trivial load on cpu. Two actions in particular account for most of
it...
* Cloning stats on every redraw. This constantly consumes 0.3% cpu on my
relay. Being lock-free is nice, but in this case a lock really makes
sense.
* Performing a flurry of addstr calls for each cell in the graph. Doing
a single vline call for each column instead drops cpu by another 0.4%.
On my relay this drops cpu usage of the graph panel from 1% to 0.4% (60%
less)...
* 0.3% cpu usage when only the header panel is rendered
* 1.3% cpu usage when the graph panel was rendered too
* 0.7% cpu usage when the graph panel was rendered with these changes
---
nyx/curses.py | 14 ++++++++++----
nyx/panel/graph.py | 39 ++++++++++++++++++++-------------------
test/panel/graph.py | 4 ++--
web/changelog/index.html | 1 +
4 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/nyx/curses.py b/nyx/curses.py
index cb47bb6..0caf8f4 100644
--- a/nyx/curses.py
+++ b/nyx/curses.py
@@ -902,17 +902,23 @@ class _Subwindow(object):
return x
- def hline(self, x, y, length, *attr):
+ def hline(self, x, y, length, *attr, **kwargs):
+ char = kwargs.get('char', curses.ACS_HLINE)
+ char = ord(char) if isinstance(char, str) else char
+
if self.width > x and self.height > y:
try:
- self._curses_subwindow.hline(max(0, y), max(0, x), curses.ACS_HLINE | curses_attr(*attr), min(length, self.width - x))
+ self._curses_subwindow.hline(max(0, y), max(0, x), char | curses_attr(*attr), min(length, self.width - x))
except:
pass
- def vline(self, x, y, length, *attr):
+ def vline(self, x, y, length, *attr, **kwargs):
+ char = kwargs.get('char', curses.ACS_VLINE)
+ char = ord(char) if isinstance(char, str) else char
+
if self.width > x and self.height > y:
try:
- self._curses_subwindow.vline(max(0, y), max(0, x), curses.ACS_VLINE | curses_attr(*attr), min(length, self.height - y))
+ self._curses_subwindow.vline(max(0, y), max(0, x), char | curses_attr(*attr), min(length, self.height - y))
except:
pass
diff --git a/nyx/panel/graph.py b/nyx/panel/graph.py
index cec0a71..e385fe6 100644
--- a/nyx/panel/graph.py
+++ b/nyx/panel/graph.py
@@ -16,6 +16,7 @@ Downloaded (0.0 B/sec): Uploaded (0.0 B/sec):
import copy
import functools
+import threading
import time
import nyx.curses
@@ -425,6 +426,7 @@ class GraphPanel(nyx.panel.Panel):
GraphStat.SYSTEM_RESOURCES: ResourceStats(),
}
+ self._stats_lock = threading.RLock()
self._stats_paused = None
if CONFIG['show_connections']:
@@ -559,18 +561,18 @@ class GraphPanel(nyx.panel.Panel):
stat = self._stats_paused[self._displayed_stat]
accounting_stats = self._accounting_stats_paused
- stat = type(stat)(stat) # clone the GraphCategory
- subgraph_height = self._graph_height + 2 # graph rows + header + x-axis label
- subgraph_width = min(subwindow.width // 2, CONFIG['max_graph_width'])
- interval, bounds_type = self._update_interval, self._bounds_type
+ with self._stats_lock:
+ subgraph_height = self._graph_height + 2 # graph rows + header + x-axis label
+ subgraph_width = min(subwindow.width // 2, CONFIG['max_graph_width'])
+ interval, bounds_type = self._update_interval, self._bounds_type
- subwindow.addstr(0, 0, stat.title(subwindow.width), HIGHLIGHT)
+ subwindow.addstr(0, 0, stat.title(subwindow.width), HIGHLIGHT)
- _draw_subgraph(subwindow, stat.primary, 0, subgraph_width, subgraph_height, bounds_type, interval, PRIMARY_COLOR)
- _draw_subgraph(subwindow, stat.secondary, subgraph_width, subgraph_width, subgraph_height, bounds_type, interval, SECONDARY_COLOR)
+ _draw_subgraph(subwindow, stat.primary, 0, subgraph_width, subgraph_height, bounds_type, interval, PRIMARY_COLOR)
+ _draw_subgraph(subwindow, stat.secondary, subgraph_width, subgraph_width, subgraph_height, bounds_type, interval, SECONDARY_COLOR)
- if stat.stat_type() == GraphStat.BANDWIDTH and accounting_stats:
- _draw_accounting_stats(subwindow, DEFAULT_CONTENT_HEIGHT + subgraph_height - 2, accounting_stats)
+ if stat.stat_type() == GraphStat.BANDWIDTH and accounting_stats:
+ _draw_accounting_stats(subwindow, DEFAULT_CONTENT_HEIGHT + subgraph_height - 2, accounting_stats)
def _update_accounting(self, event):
if not CONFIG['show_accounting']:
@@ -587,15 +589,16 @@ class GraphPanel(nyx.panel.Panel):
nyx_interface().redraw()
def _update_stats(self, event):
- for stat in self._stats.values():
- stat.bandwidth_event(event)
+ with self._stats_lock:
+ for stat in self._stats.values():
+ stat.bandwidth_event(event)
- if self._displayed_stat:
- param = self._stats[self._displayed_stat]
- update_rate = INTERVAL_SECONDS[self._update_interval]
+ if self._displayed_stat:
+ param = self._stats[self._displayed_stat]
+ update_rate = INTERVAL_SECONDS[self._update_interval]
- if param.primary.tick % update_rate == 0:
- self.redraw()
+ if param.primary.tick % update_rate == 0:
+ self.redraw()
def _draw_subgraph(subwindow, data, x, width, height, bounds_type, interval, color, fill_char = ' '):
@@ -622,9 +625,7 @@ def _draw_subgraph(subwindow, data, x, width, height, bounds_type, interval, col
for col in range(columns):
column_count = int(data.values[interval][col]) - min_bound
column_height = int(min(height - 2, (height - 2) * column_count / (max(1, max_bound) - min_bound)))
-
- for row in range(column_height):
- subwindow.addstr(x + col + axis_offset + 1, height - 1 - row, fill_char, color, HIGHLIGHT)
+ subwindow.vline(x + col + axis_offset + 1, height - column_height, column_height, color, HIGHLIGHT, char = fill_char)
def _x_axis_labels(interval, columns):
diff --git a/test/panel/graph.py b/test/panel/graph.py
index 99139e4..513a757 100644
--- a/test/panel/graph.py
+++ b/test/panel/graph.py
@@ -26,12 +26,12 @@ Download:
EXPECTED_ACCOUNTING = """
Accounting (awake) Time to reset: 01:02
- 4.7 KB / 105.2 KB 2.0 KB / 9.2 KB
+ 4.7 KB / 105.3 KB 2.0 KB / 9.3 KB
""".strip()
EXPECTED_GRAPH = """
Download:
-6 KB *
+7 KB *
*
3 KB ** *
* ****
diff --git a/web/changelog/index.html b/web/changelog/index.html
index 7977afd..686ec0e 100644
--- a/web/changelog/index.html
+++ b/web/changelog/index.html
@@ -68,6 +68,7 @@
<ul>
<li>Graph prepopulation no longer requires shifting to 15 minute intervals</li>
<li>Graphing bandwidth as bytes by default, rather than bits</li>
+ <li>Reduced cpu usage of rendering this panel by 60%</li>
</ul>
</li>
More information about the tor-commits
mailing list