summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine Pitrou <pitrou@free.fr>2017-12-02 10:07:06 +0100
committerGiampaolo Rodola <g.rodola@gmail.com>2017-12-02 10:07:06 +0100
commitf1d94cc22afdba6cb61c5a0ab246e877a83080ab (patch)
tree0ba171bfdf88a8406a6a72cb7118b6d5af489b6f
parentf0094db79ad9e4f2246997cb8c2046b71c465a29 (diff)
downloadpsutil-f1d94cc22afdba6cb61c5a0ab246e877a83080ab.tar.gz
Faster Process.children(recursive=True) (#1186)
Before: ``` $ python -m timeit -s "import psutil; p = psutil.Process()" "list(p.children(recursive=True))" 10 loops, best of 3: 29.6 msec per loop ``` After: ``` $ python -m timeit -s "import psutil; p = psutil.Process()" "list(p.children(recursive=True))" 100 loops, best of 3: 12.4 msec per loop ``` For reference, non-recursive: ``` $ python -m timeit -s "import psutil; p = psutil.Process()" "list(p.children())" 100 loops, best of 3: 12.2 msec per loop ```
-rw-r--r--psutil/__init__.py40
1 files changed, 20 insertions, 20 deletions
diff --git a/psutil/__init__.py b/psutil/__init__.py
index a8447973..9a422cda 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -888,33 +888,33 @@ class Process(object):
except (NoSuchProcess, ZombieProcess):
pass
else:
- # construct a dict where 'values' are all the processes
- # having 'key' as their parent
- table = collections.defaultdict(list)
+ # Construct a {pid: [child pids]} dict
+ reverse_ppid_map = collections.defaultdict(list)
for pid, ppid in ppid_map.items():
- try:
- p = Process(pid)
- table[ppid].append(p)
- except (NoSuchProcess, ZombieProcess):
- pass
- # At this point we have a mapping table where table[self.pid]
- # are the current process' children.
- # Below, we look for all descendants recursively, similarly
- # to a recursive function call.
- checkpids = [self.pid]
- for pid in checkpids:
- for child in table[pid]:
+ reverse_ppid_map[ppid].append(pid)
+ # Recursively traverse that dict, starting from self.pid,
+ # such that we only call Process() on actual children
+ seen = set()
+ stack = [self.pid]
+ while stack:
+ pid = stack.pop()
+ if pid in seen:
+ # Since pids can be reused while the ppid_map is
+ # constructed, there may be rare instances where
+ # there's a cycle in the recorded process "tree".
+ continue
+ seen.add(pid)
+ for child_pid in reverse_ppid_map[pid]:
try:
+ child = Process(child_pid)
# if child happens to be older than its parent
# (self) it means child's PID has been reused
intime = self.create_time() <= child.create_time()
- except (NoSuchProcess, ZombieProcess):
- pass
- else:
if intime:
ret.append(child)
- if child.pid not in checkpids:
- checkpids.append(child.pid)
+ stack.append(child_pid)
+ except (NoSuchProcess, ZombieProcess):
+ pass
return ret
def cpu_percent(self, interval=None):