From: Matthew Fernandez Date: Sun, 25 Dec 2022 22:49:52 +0000 (-0800) Subject: circogen: replace CDT 'deglist' with a generic list X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2e9423add7f60b35d8ef7991838b2ab4820008c8;p=graphviz circogen: replace CDT 'deglist' with a generic list Apart from de-duplicating some code, this has the following notable effects: 1. The list of nodes is now stored as a contiguous array rather than a linked list, improving performance characteristics. 2. The list no longer stores degrees, only node pointers. While the code using this list does modify node degrees, it does not seem to modify the degree of any node while it is present in the list. So looking up a node’s degree through the node itself saves some memory overhead. 3. Nodes are inserted into the list and _then_ the list is sorted, rather than maintaining an always-sorted list. This is a slight performance improvement. 4. The list is sorted by degree _descending_ now instead of ascending. This enables operating on the list as a stack, making the pop operation more efficient. Using `circo -Tsvg -o /dev/null swedish-flag.dot` on the example attached to #1718, we have the following: ┌──────┬───────────────┬───────────────┬──────┐ │ │ before │ after │ diff │ ╞══════╪═══════════════╪═══════════════╪══════╡ │ 1718 │ 3m34s │ 3m27s │ -3% │ │ │ 18MB peak RSS │ 18MB peak RSS │ -0% │ └──────┴───────────────┴───────────────┴──────┘ --- diff --git a/ci/clang_format.py b/ci/clang_format.py index 84788a23a..f4aeeadee 100644 --- a/ci/clang_format.py +++ b/ci/clang_format.py @@ -193,8 +193,6 @@ EXCLUDE = ( "lib/circogen/circular.c", "lib/circogen/circular.h", "lib/circogen/circularinit.c", - "lib/circogen/deglist.c", - "lib/circogen/deglist.h", "lib/circogen/edgelist.c", "lib/circogen/edgelist.h", "lib/circogen/nodelist.c", diff --git a/lib/circogen/CMakeLists.txt b/lib/circogen/CMakeLists.txt index c1b40227c..588dd00ae 100644 --- a/lib/circogen/CMakeLists.txt +++ b/lib/circogen/CMakeLists.txt @@ -8,7 +8,6 @@ add_library(circogen STATIC circo.h circpos.h circular.h - deglist.h edgelist.h nodelist.h @@ -19,7 +18,6 @@ add_library(circogen STATIC circpos.c circular.c circularinit.c - deglist.c edgelist.c nodelist.c ) diff --git a/lib/circogen/Makefile.am b/lib/circogen/Makefile.am index ac90f8094..b6e94e1e7 100644 --- a/lib/circogen/Makefile.am +++ b/lib/circogen/Makefile.am @@ -14,11 +14,11 @@ AM_CFLAGS = -DNEATOGEN_EXPORTS=1 endif noinst_HEADERS = block.h blockpath.h blocktree.h circo.h \ - circpos.h circular.h deglist.h edgelist.h nodelist.h + circpos.h circular.h edgelist.h nodelist.h noinst_LTLIBRARIES = libcircogen_C.la libcircogen_C_la_SOURCES = circularinit.c nodelist.c block.c edgelist.c \ - circular.c deglist.c blocktree.c blockpath.c \ + circular.c blocktree.c blockpath.c \ circpos.c EXTRA_DIST = gvcircogen.vcxproj* diff --git a/lib/circogen/blockpath.c b/lib/circogen/blockpath.c index ee91d462a..ecbc4854c 100644 --- a/lib/circogen/blockpath.c +++ b/lib/circogen/blockpath.c @@ -8,10 +8,10 @@ * Contributors: Details at https://graphviz.org *************************************************************************/ +#include #include #include #include -#include #include #include @@ -66,17 +66,39 @@ static Agraph_t *clone_graph(Agraph_t * ing, Agraph_t ** xg) return clone; } +DEFINE_LIST(deglist, Agnode_t*) + +/// comparison function for sorting nodes by degree, descending +static int cmpDegree(const Agnode_t **a, const Agnode_t **b) { +// Suppress Clang/GCC -Wcast-qual warnings. `DEGREE` does not modify the +// underlying data, but uses casts that make it look like it does. +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + if (DEGREE(*a) < DEGREE(*b)) { + return 1; + } + if (DEGREE(*a) > DEGREE(*b)) { + return -1; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + return 0; +} + /* fillList: - * Add nodes to deg_list, which stores them by degree. + * Add nodes to deg_list, storing them by descending degree. */ -static deglist_t *getList(Agraph_t * g) -{ - deglist_t *dl = mkDeglist(); +static deglist_t getList(Agraph_t *g) { + deglist_t dl = {0}; Agnode_t *n; for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - insertDeglist(dl, n); + deglist_append(&dl, n); } + deglist_sort(&dl, cmpDegree); return dl; } @@ -187,23 +209,22 @@ static Agraph_t *remove_pair_edges(Agraph_t * ing) int nodeCount; Agraph_t *outg; Agraph_t *g; - deglist_t *dl; Agnode_t *currnode, *adjNode; Agedge_t *e; outg = clone_graph(ing, &g); nodeCount = agnnodes(g); - dl = getList(g); + deglist_t dl = getList(g); while (counter < (nodeCount - 3)) { - currnode = firstDeglist(dl); + currnode = deglist_is_empty(&dl) ? NULL : deglist_pop(&dl); /* Remove all adjacent nodes since they have to be reinserted */ for (e = agfstedge(g, currnode); e; e = agnxtedge(g, e, currnode)) { adjNode = aghead(e); if (currnode == adjNode) adjNode = agtail(e); - removeDeglist(dl, adjNode); + deglist_remove(&dl, adjNode); } find_pair_edges(g, currnode, outg); @@ -214,8 +235,9 @@ static Agraph_t *remove_pair_edges(Agraph_t * ing) adjNode = agtail(e); DEGREE(adjNode)--; - insertDeglist(dl, adjNode); + deglist_append(&dl, adjNode); } + deglist_sort(&dl, cmpDegree); agdelete(g, currnode); @@ -223,7 +245,7 @@ static Agraph_t *remove_pair_edges(Agraph_t * ing) } agclose(g); - freeDeglist(dl); + deglist_free(&dl); return outg; } diff --git a/lib/circogen/deglist.c b/lib/circogen/deglist.c deleted file mode 100644 index 0a663628d..000000000 --- a/lib/circogen/deglist.c +++ /dev/null @@ -1,169 +0,0 @@ -/************************************************************************* - * Copyright (c) 2011 AT&T Intellectual Property - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: Details at https://graphviz.org - *************************************************************************/ - -#include -#include -#include -#include -#include - -typedef struct { - Dtlink_t link; - int deg; - Agnode_t *np; /* linked list of nodes of same degree */ -} degitem; - -static degitem *mkItem(Dt_t * d, degitem * obj, Dtdisc_t * disc) -{ - (void)d; - (void)disc; - - degitem *ap = gv_alloc(sizeof(degitem)); - - ap->np = NULL; - ap->deg = obj->deg; - return ap; -} - -static void freeItem(Dt_t * d, degitem * obj, Dtdisc_t * disc) -{ - (void)d; - (void)disc; - - free(obj); -} - -static int cmpDegree(Dt_t * d, int *key1, int *key2, Dtdisc_t * disc) -{ - (void)d; - (void)disc; - - if (*key1 < *key2) { - return -1; - } - if (*key1 > *key2) { - return 1; - } - return 0; -} - -static Dtdisc_t nodeDisc = { - offsetof(degitem, deg), /* key */ - sizeof(int), /* size */ - offsetof(degitem, link), /* link */ - (Dtmake_f) mkItem, - (Dtfree_f) freeItem, - (Dtcompar_f) cmpDegree, - (Dthash_f) 0, - (Dtmemory_f) 0, - (Dtevent_f) 0 -}; - -/* mkDeglist: - * Create an empty list of nodes. - */ -deglist_t *mkDeglist(void) -{ - deglist_t *s = dtopen(&nodeDisc, Dtoset); - return s; -} - -/* freeDeglist: - * Delete the node list. - * Nodes are not deleted. - */ -void freeDeglist(deglist_t * s) -{ - dtclose(s); -} - -/* insertDeglist: - * Add a node to the node list. - * Nodes are kept sorted by DEGREE, smallest degrees first. - */ -void insertDeglist(deglist_t * ns, Agnode_t * n) -{ - degitem key; - degitem *kp; - - key.deg = DEGREE(n); - kp = dtinsert(ns, &key); - ND_next(n) = kp->np; - kp->np = n; -} - -/* removeDeglist: - * Remove n from list, if it is in the list. - */ -void removeDeglist(deglist_t * list, Agnode_t * n) -{ - degitem key; - degitem *ip; - Agnode_t *np; - Agnode_t *prev; - - key.deg = DEGREE(n); - ip = dtsearch(list, &key); - assert(ip); - if (ip->np == n) { - ip->np = ND_next(n); - if (ip->np == NULL) - dtdelete(list, ip); - } else { - prev = ip->np; - np = ND_next(prev); - while (np && (np != n)) { - prev = np; - np = ND_next(np); - } - if (np) - ND_next(prev) = ND_next(np); - } -} - -/* firstDeglist: - * Return the first node in the list (smallest degree) - * Remove from list. - * If the list is empty, return NULL - */ -Agnode_t *firstDeglist(deglist_t * list) -{ - degitem *ip; - Agnode_t *np; - - ip = dtfirst(list); - if (ip) { - np = ip->np; - ip->np = ND_next(np); - if (ip->np == NULL) - dtdelete(list, ip); - return np; - } else - return 0; -} - -#ifdef DEBUG -void printDeglist(deglist_t * dl) -{ - degitem *ip; - node_t *np; - fprintf(stderr, " dl:\n"); - for (ip = dtfirst(dl); ip; ip = dtnext(dl, ip)) { - np = ip->np; - if (np) - fprintf(stderr, " (%d)", ip->deg); - for (; np; np = ND_next(np)) { - fprintf(stderr, " %s", agnameof(np)); - } - fprintf(stderr, "\n"); - } - -} -#endif diff --git a/lib/circogen/deglist.h b/lib/circogen/deglist.h deleted file mode 100644 index 342bd0ab4..000000000 --- a/lib/circogen/deglist.h +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************************************* - * Copyright (c) 2011 AT&T Intellectual Property - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: Details at https://graphviz.org - *************************************************************************/ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/* List of nodes sorted by increasing degree */ - -#include - - typedef Dt_t deglist_t; - - extern deglist_t *mkDeglist(void); - extern void freeDeglist(deglist_t * list); - extern void insertDeglist(deglist_t * list, Agnode_t * n); - extern void removeDeglist(deglist_t * list, Agnode_t * n); - extern Agnode_t *firstDeglist(deglist_t *); - -#ifdef DEBUG - extern void printDeglist(deglist_t *); -#endif - -#ifdef __cplusplus -} -#endif diff --git a/lib/circogen/gvcircogen.vcxproj b/lib/circogen/gvcircogen.vcxproj index 51ab142a9..e31f3cfde 100644 --- a/lib/circogen/gvcircogen.vcxproj +++ b/lib/circogen/gvcircogen.vcxproj @@ -82,7 +82,6 @@ - @@ -93,7 +92,6 @@ - diff --git a/lib/circogen/gvcircogen.vcxproj.filters b/lib/circogen/gvcircogen.vcxproj.filters index eec296e3e..63b62a6c9 100644 --- a/lib/circogen/gvcircogen.vcxproj.filters +++ b/lib/circogen/gvcircogen.vcxproj.filters @@ -33,9 +33,6 @@ Header Files - - Header Files - Header Files @@ -62,9 +59,6 @@ Source Files - - Source Files - Source Files