Skip to content

Commit 82dce5c

Browse files
tanriolBenBE
authored andcommitted
ProcessList_buildTree: sort by parent for fast search
ProcessList_buildTreeBranch used to search for children with a linear scan of the process table, which made tree build time quadratic in process count. Pre-sorting the list by parent PID (if known) makes it possible to select the correct slice by bisection much faster.
1 parent 8d98786 commit 82dce5c

File tree

2 files changed

+53
-27
lines changed

2 files changed

+53
-27
lines changed

Process.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ typedef struct Process_ {
255255
unsigned int tree_depth;
256256
unsigned int tree_index;
257257

258+
/* Has no known parent process */
259+
bool isRoot;
260+
258261
/*
259262
* Internal state for merged Command display
260263
*/

ProcessList.c

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -338,22 +338,35 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
338338
if (pid == 0)
339339
return;
340340

341-
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
342-
343-
int lastShown = 0;
344-
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
345-
Process* process = (Process*)Vector_get(this->processes, i);
346-
if (Process_isChildOf(process, pid)) {
347-
if (process->show)
348-
lastShown = Vector_size(children);
349-
Vector_add(children, process);
341+
// The vector is sorted by parent PID, find the start of the range by bisection
342+
int vsize = Vector_size(this->processes);
343+
int l = 0;
344+
int r = vsize;
345+
while (l < r) {
346+
int c = (l + r) / 2;
347+
Process* process = (Process*)Vector_get(this->processes, c);
348+
pid_t ppid = process->isRoot ? 0 : Process_getParentPid(process);
349+
if (ppid < pid) {
350+
l = c + 1;
351+
} else {
352+
r = c;
350353
}
351354
}
355+
// Find the end to know the last line for indent handling purposes
356+
int lastShown = r;
357+
while (r < vsize) {
358+
Process* process = (Process*)Vector_get(this->processes, r);
359+
if (!Process_isChildOf(process, pid))
360+
break;
361+
if (process->show)
362+
lastShown = r;
363+
r++;
364+
}
365+
366+
for (int i = l; i < r; i++) {
367+
Process* process = (Process*)Vector_get(this->processes, i);
352368

353-
int size = Vector_size(children);
354-
for (int i = 0; i < size; i++) {
355369
int index = (*node_index)++;
356-
Process* process = (Process*)Vector_get(children, i);
357370

358371
int lft = (*node_counter)++;
359372

@@ -382,7 +395,6 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
382395
process->tree_index = index;
383396
Hashtable_put(this->displayTreeSet, index, process);
384397
}
385-
Vector_delete(children);
386398
}
387399

388400
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
@@ -392,10 +404,18 @@ static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
392404
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
393405
}
394406

395-
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
407+
static int compareProcessByKnownParentThenPID(const void* v1, const void* v2) {
396408
const Process* p1 = (const Process*)v1;
397409
const Process* p2 = (const Process*)v2;
398410

411+
int result = SPACESHIP_NUMBER(
412+
p1->isRoot ? 0 : Process_getParentPid(p1),
413+
p2->isRoot ? 0 : Process_getParentPid(p2)
414+
);
415+
416+
if (result != 0)
417+
return result;
418+
399419
return SPACESHIP_NUMBER(p1->pid, p2->pid);
400420
}
401421

@@ -406,34 +426,37 @@ static void ProcessList_buildTree(ProcessList* this) {
406426
int node_counter = 1;
407427
int node_index = 0;
408428

409-
// Sort by PID
410-
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
429+
// Mark root processes
411430
int vsize = Vector_size(this->processes);
412-
413-
// Find all processes whose parent is not visible
414-
int size = Vector_size(this->processes);
415-
for (int i = 0; i < size; i++) {
431+
for (int i = 0; i < vsize; i++) {
416432
Process* process = (Process*)Vector_get(this->processes, i);
417-
418433
pid_t ppid = Process_getParentPid(process);
419-
bool isRoot = false;
434+
process->isRoot = false;
420435

421436
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
422437
// on Mac OS X 10.11.6) regard this process as root.
423438
if (process->pid == ppid)
424-
isRoot = true;
439+
process->isRoot = true;
425440

426441
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
427442
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
428443
if (!ppid)
429-
isRoot = true;
444+
process->isRoot = true;
430445

431-
// Lookup the parent via the processTable hashtable not modified in buildTree
446+
// We don't know about its parent for whatever reason
432447
if (ProcessList_findProcess(this, ppid) == NULL)
433-
isRoot = true;
448+
process->isRoot = true;
449+
}
450+
451+
// Sort by known parent PID (roots first), then PID
452+
Vector_quickSortCustomCompare(this->processes, compareProcessByKnownParentThenPID);
453+
454+
// Find all processes whose parent is not visible
455+
for (int i = 0; i < vsize; i++) {
456+
Process* process = (Process*)Vector_get(this->processes, i);
434457

435458
// If parent not found, then construct the tree with this node as root
436-
if (isRoot) {
459+
if (process->isRoot) {
437460
process = (Process*)Vector_get(this->processes, i);
438461
process->indent = 0;
439462
process->tree_depth = 0;

0 commit comments

Comments
 (0)