mirror of https://github.com/xzeldon/htop.git
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.
This commit is contained in:
parent
8d987864e4
commit
82dce5cf8e
|
@ -255,6 +255,9 @@ typedef struct Process_ {
|
||||||
unsigned int tree_depth;
|
unsigned int tree_depth;
|
||||||
unsigned int tree_index;
|
unsigned int tree_index;
|
||||||
|
|
||||||
|
/* Has no known parent process */
|
||||||
|
bool isRoot;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal state for merged Command display
|
* Internal state for merged Command display
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -338,22 +338,35 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
|
||||||
if (pid == 0)
|
if (pid == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
|
// The vector is sorted by parent PID, find the start of the range by bisection
|
||||||
|
int vsize = Vector_size(this->processes);
|
||||||
int lastShown = 0;
|
int l = 0;
|
||||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
int r = vsize;
|
||||||
Process* process = (Process*)Vector_get(this->processes, i);
|
while (l < r) {
|
||||||
if (Process_isChildOf(process, pid)) {
|
int c = (l + r) / 2;
|
||||||
|
Process* process = (Process*)Vector_get(this->processes, c);
|
||||||
|
pid_t ppid = process->isRoot ? 0 : Process_getParentPid(process);
|
||||||
|
if (ppid < pid) {
|
||||||
|
l = c + 1;
|
||||||
|
} else {
|
||||||
|
r = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Find the end to know the last line for indent handling purposes
|
||||||
|
int lastShown = r;
|
||||||
|
while (r < vsize) {
|
||||||
|
Process* process = (Process*)Vector_get(this->processes, r);
|
||||||
|
if (!Process_isChildOf(process, pid))
|
||||||
|
break;
|
||||||
if (process->show)
|
if (process->show)
|
||||||
lastShown = Vector_size(children);
|
lastShown = r;
|
||||||
Vector_add(children, process);
|
r++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = Vector_size(children);
|
for (int i = l; i < r; i++) {
|
||||||
for (int i = 0; i < size; i++) {
|
Process* process = (Process*)Vector_get(this->processes, i);
|
||||||
|
|
||||||
int index = (*node_index)++;
|
int index = (*node_index)++;
|
||||||
Process* process = (Process*)Vector_get(children, i);
|
|
||||||
|
|
||||||
int lft = (*node_counter)++;
|
int lft = (*node_counter)++;
|
||||||
|
|
||||||
|
@ -382,7 +395,6 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
|
||||||
process->tree_index = index;
|
process->tree_index = index;
|
||||||
Hashtable_put(this->displayTreeSet, index, process);
|
Hashtable_put(this->displayTreeSet, index, process);
|
||||||
}
|
}
|
||||||
Vector_delete(children);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||||
|
@ -392,10 +404,18 @@ static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||||
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
static int compareProcessByKnownParentThenPID(const void* v1, const void* v2) {
|
||||||
const Process* p1 = (const Process*)v1;
|
const Process* p1 = (const Process*)v1;
|
||||||
const Process* p2 = (const Process*)v2;
|
const Process* p2 = (const Process*)v2;
|
||||||
|
|
||||||
|
int result = SPACESHIP_NUMBER(
|
||||||
|
p1->isRoot ? 0 : Process_getParentPid(p1),
|
||||||
|
p2->isRoot ? 0 : Process_getParentPid(p2)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,34 +426,37 @@ static void ProcessList_buildTree(ProcessList* this) {
|
||||||
int node_counter = 1;
|
int node_counter = 1;
|
||||||
int node_index = 0;
|
int node_index = 0;
|
||||||
|
|
||||||
// Sort by PID
|
// Mark root processes
|
||||||
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
|
|
||||||
int vsize = Vector_size(this->processes);
|
int vsize = Vector_size(this->processes);
|
||||||
|
for (int i = 0; i < vsize; i++) {
|
||||||
// Find all processes whose parent is not visible
|
|
||||||
int size = Vector_size(this->processes);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
Process* process = (Process*)Vector_get(this->processes, i);
|
Process* process = (Process*)Vector_get(this->processes, i);
|
||||||
|
|
||||||
pid_t ppid = Process_getParentPid(process);
|
pid_t ppid = Process_getParentPid(process);
|
||||||
bool isRoot = false;
|
process->isRoot = false;
|
||||||
|
|
||||||
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
|
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
|
||||||
// on Mac OS X 10.11.6) regard this process as root.
|
// on Mac OS X 10.11.6) regard this process as root.
|
||||||
if (process->pid == ppid)
|
if (process->pid == ppid)
|
||||||
isRoot = true;
|
process->isRoot = true;
|
||||||
|
|
||||||
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
|
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
|
||||||
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
|
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
|
||||||
if (!ppid)
|
if (!ppid)
|
||||||
isRoot = true;
|
process->isRoot = true;
|
||||||
|
|
||||||
// Lookup the parent via the processTable hashtable not modified in buildTree
|
// We don't know about its parent for whatever reason
|
||||||
if (ProcessList_findProcess(this, ppid) == NULL)
|
if (ProcessList_findProcess(this, ppid) == NULL)
|
||||||
isRoot = true;
|
process->isRoot = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by known parent PID (roots first), then PID
|
||||||
|
Vector_quickSortCustomCompare(this->processes, compareProcessByKnownParentThenPID);
|
||||||
|
|
||||||
|
// Find all processes whose parent is not visible
|
||||||
|
for (int i = 0; i < vsize; i++) {
|
||||||
|
Process* process = (Process*)Vector_get(this->processes, i);
|
||||||
|
|
||||||
// If parent not found, then construct the tree with this node as root
|
// If parent not found, then construct the tree with this node as root
|
||||||
if (isRoot) {
|
if (process->isRoot) {
|
||||||
process = (Process*)Vector_get(this->processes, i);
|
process = (Process*)Vector_get(this->processes, i);
|
||||||
process->indent = 0;
|
process->indent = 0;
|
||||||
process->tree_depth = 0;
|
process->tree_depth = 0;
|
||||||
|
|
Loading…
Reference in New Issue