From fa4bd97f6e044b6294fd8b47e93f980cd61bd12c Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 08:38:13 +0100 Subject: [PATCH 1/8] Sort tree after build The startup time of xdu with big du outputs is longer than it needs to be, because the tree is kept sorted while inserting new nodes. Because of the locality of "du", we are likely to get paths with the same prefix in order (e.g. "/a/b/c/x","/a/b/c/y", "/a/b/c"). Each path is split into its components ("a","b",...) and these are seached for in the tree to find the correct parent to insert the new node. This search can greatly be reduced, if we insert new nodes as the first child of the parent, because we are very likely to search for exactly this component when we process the next entry. Always add new nodes as first child of the parent node, sort the tree once after the initial build. Performance comparison ====================== This was done with a relative small data file (144857 lines) and includes just the startup time (xmainloop disabled). Before this commit: 368.781008 task-clock (msec) # 0.728 CPUs utilized 24 context-switches # 0.065 K/sec 1 cpu-migrations # 0.003 K/sec 14561 page-faults # 0.039 M/sec 1345388200 cycles # 3.648 GHz stalled-cycles-frontend stalled-cycles-backend 2267580465 instructions # 1.69 insns per cycle 527033055 branches # 1429.122 M/sec 1899345 branch-misses # 0.36% of all branches 0.506452004 seconds time elapsed After this commit: 162.493654 task-clock (msec) # 0.546 CPUs utilized 28 context-switches # 0.172 K/sec 0 cpu-migrations # 0.000 K/sec 14567 page-faults # 0.090 M/sec 591784363 cycles # 3.642 GHz stalled-cycles-frontend stalled-cycles-backend 1155878943 instructions # 1.95 insns per cycle 258056988 branches # 1588.105 M/sec 1570485 branch-misses # 0.61% of all branches 0.297396523 seconds time elapsed --- xdu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xdu.c b/xdu.c index a45002e..5b7b33d 100644 --- a/xdu.c +++ b/xdu.c @@ -264,8 +264,8 @@ int main(int argc, char** argv) /* * dumptree(&top,0); */ - if (order != ORD_DEFAULT) - sorttree(&top, order); + + sorttree(&top, order); topp = ⊤ /* @@ -507,7 +507,9 @@ void addtree(struct node* top, char* path[], long long size) * no child matched, add a new child */ np = makenode(path[0], -1); - insertchild(top, np, order); + np->parent = top; + np->peer = top->child; + top->child=np; if (path[1] == NULL) { /* From 2e074ca9150474fb6d4da2ce1a9cb02bc26d349b Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 09:22:19 +0100 Subject: [PATCH 2/8] Remove obsolete function insertchild This function has been obsoleted by the previous commit. Remove it. --- xdu.c | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/xdu.c b/xdu.c index 5b7b33d..4cc3dc7 100644 --- a/xdu.c +++ b/xdu.c @@ -421,55 +421,6 @@ int compare(struct node* n1, struct node* n2, int order) return 0; } -void insertchild(struct node* nodep, struct node* childp, int order) -{ - struct node *np, - *np1; - - if (nodep == NODE_NULL || childp == NODE_NULL) - return; - if (childp->peer != NODE_NULL) { - fprintf(stderr, "xdu: can't insert child with peers\n"); - return; - } - - childp->parent = nodep; - if (nodep->child == NODE_NULL) { - /* - * no children, order doesn't matter - */ - nodep->child = childp; - return; - } - /* - * nodep has at least one child already - */ - if (compare(childp, nodep->child, order) < 0) { - /* - * new first child - */ - childp->peer = nodep->child; - nodep->child = childp; - return; - } - np1 = nodep->child; - for (np = np1->peer; np != NODE_NULL; np = np->peer) { - if (compare(childp, np, order) < 0) { - /* - * insert between np1 and np - */ - childp->peer = np; - np1->peer = childp; - return; - } - np1 = np; - } - /* - * at end, link new child on - */ - np1->peer = childp; -} - /* * add path as a child of top - recursively */ From 3a00b22d3700eba4c00d9c460e906ffafc0ef95d Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 09:20:20 +0100 Subject: [PATCH 3/8] Declare internal routines as static Use "static" functions where possible to give the compiler more freedom for optimizations. --- xdu.c | 60 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/xdu.c b/xdu.c index 4cc3dc7..7347dea 100644 --- a/xdu.c +++ b/xdu.c @@ -88,12 +88,12 @@ int ncols = NCOLS; * internal routines */ // char *strdup(); -void addtree(); -void parse_file(); -void parse_entry(); +static void addtree(); +static void parse_file(); +static void parse_entry(); void dumptree(); -void clearrects(); -void sorttree(); +static void clearrects(); +static void sorttree(); /* * order to sort paths by @@ -139,7 +139,7 @@ long nnodes = 0; /* * create a new node with the given name and size info */ -struct node* +static struct node* makenode(char* name, long long size) { struct node* np; @@ -157,7 +157,7 @@ makenode(char* name, long long size) * Return the node (if any) which has a draw rectangle containing * the given x,y point. */ -struct node* +static struct node* findnode(struct node* treep, int x, int y) { struct node* np; @@ -188,7 +188,7 @@ findnode(struct node* treep, int x, int y) /* * return a count of the number of children of a given node */ -int numchildren(struct node* nodep) +static int numchildren(struct node* nodep) { int n; @@ -207,7 +207,7 @@ int numchildren(struct node* nodep) * had their sizes initialized. [DPT911113] * * * * This function is recursive * * * */ -long fix_tree(struct node* top) +static long fix_tree(struct node* top) { struct node* nd; @@ -278,7 +278,7 @@ int main(int argc, char** argv) exit(0); } -void parse_file(char* filename) +static void parse_file(char* filename) { char buf[4096]; char name[4096]; @@ -317,7 +317,7 @@ void parse_file(char* filename) /* * bust up a path string and link it into the tree */ -void parse_entry(char* name, long long size) +static void parse_entry(char* name, long long size) { char* path[MAXDEPTH]; /* break up path into this list */ char buf[MAXNAME]; /* temp space for path element @@ -369,7 +369,7 @@ void parse_entry(char* name, long long size) * 0 if it is a toss up. * 1 if it should go after. */ -int compare(struct node* n1, struct node* n2, int order) +static int compare(struct node* n1, struct node* n2, int order) { switch (order) { case ORD_SIZE: @@ -424,7 +424,7 @@ int compare(struct node* n1, struct node* n2, int order) /* * add path as a child of top - recursively */ -void addtree(struct node* top, char* path[], long long size) +static void addtree(struct node* top, char* path[], long long size) { struct node* np; @@ -493,7 +493,7 @@ void dumptree(struct node* np, int level) } } -void sorttree(struct node* np, int order) +static void sorttree(struct node* np, int order) { struct node* subnp; struct node *np0, @@ -541,7 +541,7 @@ void sorttree(struct node* np, int order) * Draws all children of a node within the given rectangle. * Recurses on children. */ -void drawchildren(struct node* nodep, struct rect rect) +static void drawchildren(struct node* nodep, struct rect rect) { long long totalsize; int totalheight; @@ -618,7 +618,7 @@ void drawchildren(struct node* nodep, struct rect rect) * Draws a node in the given rectangle, and all of its children * to the "right" of the given rectangle. */ -void drawnode(struct node* nodep, struct rect rect) +static void drawnode(struct node* nodep, struct rect rect) { struct rect subrect; @@ -651,7 +651,7 @@ void drawnode(struct node* nodep, struct rect rect) * clear the rectangle information of a given node * and all of its decendents */ -void clearrects(struct node* nodep) +static void clearrects(struct node* nodep) { struct node* np; @@ -671,7 +671,7 @@ void clearrects(struct node* nodep) } } -void pwd(void) +static void pwd(void) { struct node* np; struct node* stack[MAXDEPTH]; @@ -702,7 +702,7 @@ void pwd(void) } #ifdef NEED_STRDUP -char* strdup(char* s) +static char* strdup(char* s) { int n; char* cp; @@ -874,7 +874,7 @@ XDU Version %s - Keyboard Commands\n\ XDU_VERSION); } -void fprintpsstart(FILE* fp) +static void fprintpsstart(FILE* fp) { fprintf(fp, "%%!\n" @@ -901,7 +901,7 @@ void fprintpsstart(FILE* fp) "/xduorigctm matrix currentmatrix def\n\n"); } -void fprintpsend(FILE* fp) +static void fprintpsend(FILE* fp) { fprintf(fp, "grestore\n" @@ -914,7 +914,7 @@ void fprintpsend(FILE* fp) "%%%%EOF\n"); } -void fprintpsbox(FILE* fp, int x1, int y1, int x2, int y2) +static void fprintpsbox(FILE* fp, int x1, int y1, int x2, int y2) { fprintf(fp, "%%BOX\n" @@ -932,7 +932,7 @@ void fprintpsbox(FILE* fp, int x1, int y1, int x2, int y2) x1, y1, x2 + x1, y1, x2 + x1, y2 + y1, x1, y2 + y1); } -void fprintpstext(FILE* fp, int x, int y, char* s) +static void fprintpstext(FILE* fp, int x, int y, char* s) { fprintf(fp, "%%TEXT\n" @@ -945,7 +945,7 @@ void fprintpstext(FILE* fp, int x, int y, char* s) x, y, s); } -void savepschildren(FILE* fp, struct node* nodep, struct rect rect, int showsize) +static void savepschildren(FILE* fp, struct node* nodep, struct rect rect, int showsize) { long long size, totalsize; int totalheight, @@ -1016,7 +1016,7 @@ void savepschildren(FILE* fp, struct node* nodep, struct rect rect, int showsize } } -void savepsnode(FILE* fp, struct node* nodep, struct rect rect, int showsize) +static void savepsnode(FILE* fp, struct node* nodep, struct rect rect, int showsize) { struct rect subrect; char label[1024], @@ -1081,24 +1081,24 @@ void savetops(char* fname, int showsize) } } -void fprintsvgstart(FILE* fp, int width, int height) +static void fprintsvgstart(FILE* fp, int width, int height) { fprintf(fp, "\n\n", width, height); } -void fprintsvgend(FILE* fp) +static void fprintsvgend(FILE* fp) { fprintf(fp, ""); } -void fprintsvgnode(FILE* fp, struct rect rect) +static void fprintsvgnode(FILE* fp, struct rect rect) { fprintf(fp, " \n", rect.left, rect.top, rect.width, rect.height); } -void fprintsvgnodetext(FILE* fp, int x, int y, long long size, char* name, int showsize) +static void fprintsvgnodetext(FILE* fp, int x, int y, long long size, char* name, int showsize) { char buffer[1024], *text="n/a"; @@ -1135,7 +1135,7 @@ void fprintsvgnodetext(FILE* fp, int x, int y, long long size, char* name, int s x, y, text); } -void savesvgnode(FILE* fp, struct node* nodep, int showsize, int depth) +static void savesvgnode(FILE* fp, struct node* nodep, int showsize, int depth) { struct node* np; From 18c9542f0011e16d0a8b1804b7e0cbf29c1e169c Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 12:59:08 +0100 Subject: [PATCH 4/8] Revert "Little improvment for repainting" Revert the timer based redraw prevention, because we want to take a new approach based on an idle worker. This reverts commit 99a44ddc5ddc659345332be8552ed18ad3f68878. --- xwin.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/xwin.c b/xwin.c index a2a08bc..b60ea4d 100644 --- a/xwin.c +++ b/xwin.c @@ -35,8 +35,6 @@ #include /* for exit() */ #endif -#include - /* * IMPORTS: routines that this module vectors out to */ @@ -69,11 +67,6 @@ extern void xdrawrect(); */ static void help_popup(); static void help_popdown(); -static struct timeval last_repaint; -static long int timediff(struct timeval* t1, struct timeval* t2) -{ - return 1000000 * (t2->tv_sec - t1->tv_sec) + t2->tv_usec - t1->tv_usec; -} static String fallback_resources[] = { "*window.width: 600", @@ -345,20 +338,10 @@ static void c_resize(Widget w, XtPointer data, XEvent* event, Boolean* continue_to_dispatch) { UNUSED(w, data, event, continue_to_dispatch); - /* - * printf("Resize\n"); - */ - long int diff; - struct timeval t; - - gettimeofday(&t, NULL); - diff = timediff(&last_repaint, &t); - - if (diff < (long int)50000) - return; + /* + * printf("Resize\n"); + */ xrepaint(); - - gettimeofday(&last_repaint, NULL); } static void @@ -368,14 +351,6 @@ c_repaint(Widget w, XtPointer data, XEvent* event, Boolean* continue_to_dispatch /* * printf("Expose\n"); */ - long int diff; - struct timeval t; - - gettimeofday(&t, NULL); - diff = timediff(&last_repaint, &t); - - if (diff < (long int)50000) - return; xrepaint_noclear(); } From d3dc43d65a5386f28664981ea992396354975cbb Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 14:20:11 +0100 Subject: [PATCH 5/8] Remove resize callback A resize triggers an Expose event and we are going to handle changed windows sizes there. So remove resize callback. --- xwin.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/xwin.c b/xwin.c index b60ea4d..f74cfb1 100644 --- a/xwin.c +++ b/xwin.c @@ -334,15 +334,6 @@ a_removehelp(Widget w, XEvent* event, String* params, Cardinal* num_params) * callback routines */ -static void -c_resize(Widget w, XtPointer data, XEvent* event, Boolean* continue_to_dispatch) -{ - UNUSED(w, data, event, continue_to_dispatch); - /* - * printf("Resize\n"); - */ - xrepaint(); -} static void c_repaint(Widget w, XtPointer data, XEvent* event, Boolean* continue_to_dispatch) @@ -440,7 +431,6 @@ int xsetup(int* argcp, char** argv) * events */ XtAddEventHandler(w, ExposureMask, False, c_repaint, NULL); - XtAddEventHandler(w, StructureNotifyMask, False, c_resize, NULL); XtAugmentTranslations(w, trans_table); XtRealizeWidget(toplevel); From 4f22d4d5ea7471a130f4a3d7211d5b6ed4f6f97a Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 14:30:34 +0100 Subject: [PATCH 6/8] Add setRepaintWhenIdle() Add a function which will register a (Idle-) work procedure to do the redraw when there are no more other events pending. If the window size has changed after the last redraw, clear the window to clean up wrongly scaled relicts. --- xwin.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/xwin.c b/xwin.c index f74cfb1..6e46f31 100644 --- a/xwin.c +++ b/xwin.c @@ -354,6 +354,31 @@ static Visual* vis; static Window win; static GC gc; static XtAppContext app_con; +static int workerRegistered=0; +static int last_repaint_width=-1; +static int last_repaint_height=-1; + +Boolean doIdleRepaint(XtPointer data) { + (void)data; + XWindowAttributes xwa; + + XGetWindowAttributes(dpy, win, &xwa); + if (xwa.width != last_repaint_width || xwa.height != last_repaint_height) { + XClearWindow(dpy, win); + } + repaint(xwa.width, xwa.height); + last_repaint_width=xwa.width; + last_repaint_height=xwa.height; + workerRegistered=0; + return True; +} + +static void setRepaintWhenIdle() { + if (!workerRegistered) { + (void)XtAppAddWorkProc(app_con,doIdleRepaint,NULL); + workerRegistered=1; + } +} static void a_savesvg(Widget w, XEvent* e, String* params, Cardinal* num_params) From 1ed3a0bc9a64339a0d06ded9845a0a4880d1cab8 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 15:41:13 +0100 Subject: [PATCH 7/8] Use work procedure on Expose events. Instead of doing a repaint on every Expose event, just trigger the work procedure to do a repaint when the queue is idle. --- xwin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xwin.c b/xwin.c index 6e46f31..5dd7e7d 100644 --- a/xwin.c +++ b/xwin.c @@ -333,7 +333,7 @@ a_removehelp(Widget w, XEvent* event, String* params, Cardinal* num_params) /* * callback routines */ - +static void setRepaintWhenIdle(); static void c_repaint(Widget w, XtPointer data, XEvent* event, Boolean* continue_to_dispatch) @@ -342,7 +342,7 @@ c_repaint(Widget w, XtPointer data, XEvent* event, Boolean* continue_to_dispatch /* * printf("Expose\n"); */ - xrepaint_noclear(); + setRepaintWhenIdle(); } /* From 1b8346634160585141a688988f3ce349a6ad78e2 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 22 Mar 2019 14:41:40 +0100 Subject: [PATCH 8/8] Remove xrepaint_noclear Remove the now obsolete function. --- xwin.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/xwin.c b/xwin.c index 5dd7e7d..5f9751f 100644 --- a/xwin.c +++ b/xwin.c @@ -59,7 +59,6 @@ extern int xsetup(); extern int xmainloop(); extern void xclear(); extern void xrepaint(); -extern void xrepaint_noclear(); extern void xdrawrect(); /* @@ -498,14 +497,6 @@ void xrepaint() repaint(xwa.width, xwa.height); } -void xrepaint_noclear() -{ - XWindowAttributes xwa; - - XGetWindowAttributes(dpy, win, &xwa); - repaint(xwa.width, xwa.height); -} - void readable_float(float number, char* number_label) { char number_string[1024];