]> granicus.if.org Git - graphviz/commitdiff
circogen: replace CDT 'deglist' with a generic list
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Sun, 25 Dec 2022 22:49:52 +0000 (14:49 -0800)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Sat, 31 Dec 2022 21:40:20 +0000 (13:40 -0800)
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% │
  └──────┴───────────────┴───────────────┴──────┘

ci/clang_format.py
lib/circogen/CMakeLists.txt
lib/circogen/Makefile.am
lib/circogen/blockpath.c
lib/circogen/deglist.c [deleted file]
lib/circogen/deglist.h [deleted file]
lib/circogen/gvcircogen.vcxproj
lib/circogen/gvcircogen.vcxproj.filters

index 84788a23a7e68417d803249a73a4c579f873a5c2..f4aeeadeeadba863c61971d7c6c54bd25cfdab6b 100644 (file)
@@ -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",
index c1b40227c05215c7c3878a78dd183e56b8f96d1d..588dd00aea4486d97fb921c809749dd9f8627ea4 100644 (file)
@@ -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
 )
index ac90f8094ccd15704aae1b58daefe615037a61d3..b6e94e1e72287cb04f09c9c0b44b8761af1a0155 100644 (file)
@@ -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*
index ee91d462a4b950892f67e3cc0b3d9d4547709095..ecbc4854cee08bcdb5ad0561ed155a35cb050e13 100644 (file)
@@ -8,10 +8,10 @@
  * Contributors: Details at https://graphviz.org
  *************************************************************************/
 
+#include       <cgraph/list.h>
 #include       <cgraph/alloc.h>
 #include       <circogen/blockpath.h>
 #include       <circogen/edgelist.h>
-#include       <circogen/deglist.h>
 #include       <stddef.h>
 #include       <stdbool.h>
 
@@ -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 (file)
index 0a66362..0000000
+++ /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 <cgraph/alloc.h>
-#include <circogen/deglist.h>
-#include <circogen/circular.h>
-#include <circogen/blockpath.h>
-#include <assert.h>
-
-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 (file)
index 342bd0a..0000000
+++ /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  <render.h>
-
-    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
index 51ab142a91ccdbcaf209a85666a2e58fb1389b7e..e31f3cfde197b9f87233bb0d56d20b9055221b5e 100644 (file)
@@ -82,7 +82,6 @@
     <ClInclude Include="circo.h" />
     <ClInclude Include="circpos.h" />
     <ClInclude Include="circular.h" />
-    <ClInclude Include="deglist.h" />
     <ClInclude Include="edgelist.h" />
     <ClInclude Include="nodelist.h" />
   </ItemGroup>
@@ -93,7 +92,6 @@
     <ClCompile Include="circpos.c" />
     <ClCompile Include="circular.c" />
     <ClCompile Include="circularinit.c" />
-    <ClCompile Include="deglist.c" />
     <ClCompile Include="edgelist.c" />
     <ClCompile Include="nodelist.c" />
   </ItemGroup>
index eec296e3e455251902ab99b3ba5c0492f9376a19..63b62a6c9fa17e45a4bf1348de08a8e0af0d9497 100644 (file)
@@ -33,9 +33,6 @@
     <ClInclude Include="circular.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="deglist.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="edgelist.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -62,9 +59,6 @@
     <ClCompile Include="circularinit.c">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="deglist.c">
-      <Filter>Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="edgelist.c">
       <Filter>Source Files</Filter>
     </ClCompile>