Skip to content

Commit 48f1672

Browse files
committed
New approach
1 parent 34005ae commit 48f1672

File tree

1 file changed

+29
-36
lines changed

1 file changed

+29
-36
lines changed

nbresuse/__init__.py

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import json
33
import psutil
4-
from traitlets import Float, Int, Union, default
4+
from traitlets import Bool, Float, Int, Union, default
55
from traitlets.config import Configurable
66
from notebook.utils import url_path_join
77
from notebook.base.handlers import IPythonHandler
@@ -15,46 +15,37 @@
1515

1616
from threading import Thread
1717
from concurrent.futures import ThreadPoolExecutor, as_completed
18+
from tornado.concurrent import run_on_executor
1819

1920
class MetricsHandler(IPythonHandler):
2021
def initialize(self):
2122
super().initialize()
2223
self.cpu_percent = 0
24+
25+
# https://www.tornadoweb.org/en/stable/concurrent.html#tornado.concurrent.run_on_executor
26+
self.executor = ThreadPoolExecutor(max_workers=10)
27+
2328
# A better approach would use cpu_affinity to account for the
2429
# fact that the number of logical CPUs in the system is not
2530
# necessarily the same as the number of CPUs the process
2631
# can actually use. But cpu_affinity isn't available for OS X.
2732
self.cpu_count = psutil.cpu_count()
2833

29-
def update_cpu_percent():
30-
def get_cpu_percent(p):
31-
try:
32-
return p.cpu_percent(interval=0.1)
33-
# Avoid littering logs with stack traces complaining
34-
# about dead processes having no CPU usage
35-
except:
36-
return 0
37-
# This loop should execute roughly every "interval" seconds
38-
# Slower if max_workers is much less than the number of processes
39-
while True:
40-
cur_process = psutil.Process()
41-
all_processes = [cur_process] + cur_process.children(recursive=True)
42-
# Could have a worker for every process
43-
with ThreadPoolExecutor(max_workers=10) as executor:
44-
cpu_percents = [executor.submit(get_cpu_percent, p) for p in all_processes]
45-
total_percent = 0
46-
for future in as_completed(cpu_percents):
47-
try:
48-
total_percent += future.result()
49-
except:
50-
pass
51-
self.cpu_percent = total_percent
52-
53-
t = Thread(target=update_cpu_percent)
54-
t.start()
34+
@run_on_executor
35+
def update_cpu_percent(self, all_processes):
36+
37+
def get_cpu_percent(p):
38+
try:
39+
return p.cpu_percent(interval=0.05)
40+
# Avoid littering logs with stack traces complaining
41+
# about dead processes having no CPU usage
42+
except:
43+
return 0
44+
45+
return sum([get_cpu_percent(p) for p in all_processes])
5546

5647
@web.authenticated
57-
def get(self):
48+
async def get(self):
5849
"""
5950
Calculate and return current resource usage metrics
6051
"""
@@ -68,20 +59,15 @@ def get(self):
6859
else: # mem_limit is an Int
6960
mem_limit = config.mem_limit
7061

71-
def get_cpu_percent(p):
72-
try:
73-
return p.cpu_percent(interval=0.1)
74-
# Avoid littering logs with stack traces complaining
75-
# about dead processes having no CPU usage
76-
except:
77-
return 0
78-
cpu_percent = sum([get_cpu_percent(p) for p in all_processes])
7962
# A better approach would use cpu_affinity to account for the
8063
# fact that the number of logical CPUs in the system is not
8164
# necessarily the same as the number of CPUs the process
8265
# can actually use. But cpu_affinity isn't available for OS X.
8366
cpu_count = psutil.cpu_count()
8467

68+
if config.track_cpu_percent:
69+
self.cpu_percent = await self.update_cpu_percent(all_processes)
70+
8571
limits = {}
8672

8773
if config.mem_limit != 0:
@@ -158,6 +144,13 @@ class ResourceUseDisplay(Configurable):
158144
"""
159145
).tag(config=True)
160146

147+
track_cpu_percent = Bool(
148+
False,
149+
help="""
150+
Set to True in order to enable reporting of CPU usage statistics.
151+
"""
152+
).tag(config=True)
153+
161154
cpu_warning_threshold = Float(
162155
0.1,
163156
help="""

0 commit comments

Comments
 (0)