Improving the GP 2 Compiler
aa r X i v : . [ c s . P L ] J a n Improving the GP 2 Compiler
Graham Campbell , Jack Romo , and
Detlef PlumpDepartment of Computer ScienceUniversity of York, United Kingdom
September 2019Revised February 2020 Supported by a Vacation Internship from the Engineering and Physical SciencesResearch Council (EPSRC) in the UK. bstract
GP 2 is an experimental programming language based on graph transforma-tion rules which aims to facilitate program analysis and verification. Writingefficient programs in such a language is hard because graph matching is ex-pensive, however GP 2 addresses this problem by providing rooted rules which,under mild conditions, can be matched in constant time using the GP 2 to Ccompiler. In this report, we document various improvements made to thecompiler; most notably the introduction of node lists to improve iterationperformance for destructive programs, meaning that binary DAG recognitionby reduction need only take linear time where the previous implementationrequired quadratic time. ontents
List of Figures viiExecutive Summary ixPreface xi1 Introduction 1
CONTENTS
A Usage Documentation 29
A.1 Legacy Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . 29A.2 New Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31A.3 Dockerized Version . . . . . . . . . . . . . . . . . . . . . . . . . 31
B Software Testing 33C Benchmarking Details 35Bibliography 37 ist of Figures is-discrete.gp2 . . . . . . . . . . . . . . . . 41.2 Measured Performance of is-discrete.gp2 . . . . . . . . . 52.1 Example Rooted Derivation . . . . . . . . . . . . . . . . . . . . 133.1 Input Graph Classes . . . . . . . . . . . . . . . . . . . . . . . . 153.2 GP 2 Program is-bin-dag.gp2 . . . . . . . . . . . . . . . . 163.3 Measured Performance of is-bin-dag.gp2 . . . . . . . . . . 163.4 GP 2 Program is-tree.gp2 . . . . . . . . . . . . . . . . . . . 173.5 Measured Performance of is-tree.gp2 . . . . . . . . . . . . 173.6 GP 2 Program is-series-par.gp2 . . . . . . . . . . . . . . 183.7 Measured Performance of is-series-par.gp2 . . . . . . . . 183.8 Generated Graph Classes . . . . . . . . . . . . . . . . . . . . . 183.9 GP 2 Program gen-discrete.gp2 . . . . . . . . . . . . . . . 193.10 GP 2 Program gen-tree.gp2 . . . . . . . . . . . . . . . . . . 193.11 GP 2 Program gen-star.gp2 . . . . . . . . . . . . . . . . . . 193.12 GP 2 Program gen-sierpinski.gp2 . . . . . . . . . . . . . 203.13 Measured Generation Performance . . . . . . . . . . . . . . . . 203.14 GP 2 Program is-con.gp2 . . . . . . . . . . . . . . . . . . . 213.15 Measured Performance of is-con.gp2 . . . . . . . . . . . . . 213.16 GP 2 Program trans-closure.gp2 . . . . . . . . . . . . . . 213.17 Measured Performance of trans-closure.gp2 . . . . . . . . 22A.1 The gp2i.proto
Interface Description . . . . . . . . . . . . . 32viiiii
LIST OF FIGURES
B.1 The iso.gp2
Pseudo-Program . . . . . . . . . . . . . . . . . . 34C.1 The config.proto
Interface Description . . . . . . . . . . . . 35C.2 The gen.proto
Interface Description . . . . . . . . . . . . . . 36C.3 The bench.proto
Interface Description . . . . . . . . . . . . 36 xecutive Summary
GP 2 is an experimental rule-based programming language based on graphtransformation rules which aims to facilitate program analysis and verific-ation. Writing efficient programs in a rule-based language is hard becausegraph matching is expensive, however GP 2 addresses this problem by provid-ing rooted rules which, under mild conditions, can be matched in constanttime by the code generated by the GP 2 to C compiler, to which we have:1. Introduced node and edge lists to improve iteration performance fordestructive programs, allowing the compiler to produce node matchingcode that skips over an area of deleted nodes in constant time. Thisallows us to recognise binary DAGs in linear time (given an input graphof bounded degree) by means of a slick reduction algorithm, where inthe previous compiler implementation, such a program would have aquadratic worst case time complexity.2. Fixed parsing input graphs of arbitrary size using Judy arrays. In theprevious implementation, one had to specify the maximum number ofnodes and edges expected in input host graphs, and the generated codewould allocate memory for such a graph on startup, before parsing theinput graph. In our updated implementation, it is no longer required toknow the maximum input graph size at compile time; we dynamicallygrow the memory needed during parsing.3. Added a way to shutdown the generated program as soon as it has writ-ten the output graph without cleaning up all the allocated heap memoryin a proper way. In general, proper management of the heap memory isa good idea to avoid bugs due to memory leaks or otherwise, however,one might not want to do this in the interest of runtime performance, toallow the process to exit as soon as possible after writing the output.4. Added a way to ensure matches both reflect root nodes, as well as pre-serving them. By default, GP 2 will allow a non-root in a rule LHS tomatch against either a non-root or a root in a host graph, but if weinsist on matches reflecting root nodes, matches can only match non-roots against non-roots. This is useful in order to achieve reversibilityof derivations and also to have more control over rule application.ix
EXECUTIVE SUMMARY
We conjecture that the new compiler implementation has worst case timecomplexity no worse than the original implementation. Moreover, we provideruntime performances of various programs and graph classes as empirical evid-ence for our conjecture, where timing results differ only by a constant factor.Reduction programs on disconnected graphs are massively improved, changingcomplexity class, programs that use edge searching heavily, see a runtime per-formance improvement, such as DFS and transitive closure, and most otherprograms see a small performance degradation, which we consider worth it forthe benefits. reface
This report summarises the improvements made to the GP 2 compiler duringAugust and September 2019. This work was, in part, funded by a Vacation In-ternship of the Engineering and Physical Sciences Research Council (EPSRC)granted to Graham Campbell. The structure of the report is as follows:1. We start by very briefly introduce algebraic graph transformation, theGP 2 language, and the GP 2 to C compiler.2. We will then detail our improvements to the compiler, motivating thechanges for each change.3. Next, we give timing results comparing the original implementation tothe improvement implementation, commenting on why it is faster andslower on various classes of program.4. Finally, we will summarise and evaluate our results.In the appendices, we have provided usage documentation for current theGP 2 Compiler, details of the test suite used to give confidence in the cor-rectness of the new implementation, and details of the benchmarking softwareincluding the programs used to generate the input graphs.xiii
PREFACE hapter 1
Introduction
In this chapter, we very briefly review the different approaches to algebraicgraph transformation, in order to introduce the language GP 2 and its com-piler.
Graph transformation is the rule-based modification of graphs, and is a dis-cipline dating back to the 1970s. There are various approaches to graph trans-formation, most notably the ‘node replacement’ [1], ‘edge replacement’ [2], and‘algebraic’ approaches [3], [4], originally developed at the Technical Universityof Berlin by Ehrig, Pfender, and Schneider [5], [6]. The two major approachesto algebraic graph transformation are the so called ‘double pushout’ (DPO)approach, and the ‘single pushout’ (SPO) approach.Because the DPO approach operates in a structure-preserving manner (ruleapplication in SPO is without an interface graph, so there are no danglingcondition checks), this approach is more widely used than the SPO [4], [7].Moreover, the DPO approach is genuinely local in the sense that each rule ap-plication can only modify the local area of the graph in which it is matched, asopposed to SPO, which allows arbitrary implicit edge deletion. More recently,there have been hybrid approaches that attempt to gain the locality of DPO,but the flexibility of SPO, such as the Sesqui-Pushout approach [8], [9], whichis compatible with DPO when we have injective matches and linear rules [10].There are a number of languages and tools, such as AGG [11], GMTE [12],Dactl [13], GP 2 [14], GReAT [15], GROOVE [16], GrGen.Net [17], Henshin[18], PROGRES [19], and PORGY [20]. It is reasonable that a general purposelocal graph transformation language should choose the DPO approach withinjective matches and linear rules; GP 2 is such a language. Moreover, Habeland Plump show that such languages can be ‘computationally complete’ [21].1
CHAPTER 1. INTRODUCTION
GP 2 is an experimental non-deterministic rule-based language for problemsolving in the domain of graphs, developed at York, the successor of GP [14],[22]. GP 2 is of interest because it has been designed to support formal reas-oning on programs [23], with a semantics defined in terms of partially labelledgraphs, using the injective DPO approach with linear rules and relabelling[10], [24].GP 2 programs transform input graphs into output graphs, where graphsare directed and may contain parallel edges and loops. Both nodes and edgesare labelled with lists consisting of integers and character strings. This in-cludes the special case of items labelled with the empty list. The principalprogramming construct in GP 2 consist of conditional graph transformationrules labelled with expressions. Rules operate on ‘host graphs’ which are la-belled with constant values. Formally, the application of a rule to a host graphis defined as a two-stage process in which first the rule is instantiated by repla-cing all variables with values of the same type, and evaluating all expressions.This yields a standard rule (without expressions) in the DPO approach withrelabelling. In the second stage, the instantiated rule is applied to the hostgraph by constructing two suitable pushouts. The formal semantics of GP 2 isgiven in the style of Plotkin’s structural operational semantics [25]. Inferencerules, first given in [14], inductively define a small-step transition relation onconfigurations. Up-to-date versions can be found in Bak’s Thesis [26].Intuitively, applying a rule L ⇒ R to a host graph G works as follows:(1) Replace the variables in L and R with constant values and evaluate theexpressions in L and R , to obtain an instantiated rule ˆ L ⇒ ˆ R . (2) Choosea subgraph S of G isomorphic to ˆ L such that the dangling condition andthe rule’s application condition are satisfied (see below). (3) Replace S withˆ R as follows: numbered nodes stay in place (possibly relabelled), edges andunnumbered nodes of ˆ L are deleted, and edges and unnumbered nodes of ˆ R are inserted. In this construction, the ‘dangling condition’ requires that nodesin S corresponding to unnumbered nodes in ˆ L (which should be deleted) mustnot be incident with edges outside S . The rule’s application condition isevaluated after variables have been replaced with the corresponding values ofˆ L , and node identifiers of L with the corresponding identifiers of S .A program consists of declarations of conditional rules and procedures,and exactly one declaration of a main command sequence, which is a distinctprocedure named Main . Procedures must be non-recursive, they can be seenas macros. We describe GP 2’s main control constructs. The call of a ruleset { r , . . . , r n } non-deterministically applies one of the rules whose left-handgraph matches a subgraph of the host graph such that the dangling conditionand the rule’s application condition are satisfied. The call ‘fails’ if none of therules is applicable to the host graph. The command if C then P else Q .3. ROOTED GP 2 PROGRAMS G by first executing C on a copy of G . If thisresults in a graph, P is executed on the original graph G ; otherwise, if C fails, Q is executed on G . The try command has a similar effect, except that P isexecuted on the result of C ’s execution. The loop command P ! executes thebody P repeatedly until it fails. When this is the case, P ! terminates withthe graph on which the body was entered for the last time. The break com-mand inside a loop terminates that loop and transfers control to the commandfollowing the loop.Poskitt and Plump have set up the foundations for verification of GP 2programs [27]–[30] using a Hoare-Style [31] system (actually for GP [22], [32]),Hristakiev and Plump have developed static analysis for confluence checking[33], [34], and Bak and Plump have extended the language, adding root nodes[26], [35]. Plump has shown computational completeness [36]. Most recently,Atkinson, Plump, and Stepney have developed a probabilistic extension toGP 2 [37], [38]. Most recently, Campbell, Courtehoute and Plump have beeninterested in linear time algorithms in GP 2 [39], motivating some of the workin this report. We also build on our earlier work in [40]. The bottleneck for efficiently implementing algorithms in a language based ongraph transformation rules is the cost of graph matching. In general, to matchthe left-hand graph L of a rule within a host graph G requires time polynomialin the size of L [35]. As a consequence, linear-time graph algorithms in imper-ative languages may be slowed down to polynomial time when they are recastas rule-based programs. To speed up matching, GP 2 supports ‘rooted’ graphtransformation where graphs in rules and host graphs are equipped with so-called root nodes, originally developed by Dörr [41]. Roots in rules must matchroots in the host graph so that matches are restricted to the neighbourhoodof the host graph’s roots. We draw root nodes using double circles.A conditional rule ( L ⇒ R, c ) is ‘fast’ if (1) each node in L is undirectedlyreachable from some root, (2) neither L nor R contain repeated occurrencesof list, string or atom variables, and (3) the condition c contains neither an edge predicate nor a test e = e or e ! = e where both e and e containa list, string or atom variable. Conditions (2) and (3) will be satisfied by allrules occurring in the following sections; in particular, we neither use the edge predicate nor the equality tests. Theorem 1.1 (Complexity of matching fast rules [35]) . Rooted graph match-ing can be implemented to run in constant time for fast rules, provided thereare upper bounds on the maximal node degree and the number of roots inhost graphs.
CHAPTER 1. INTRODUCTION
Before we discuss our modifications to the GP 2 Compiler , we first outlineits prior state. The compiler detailed in Bak’s Thesis [26] compiled GP 2programs into C code with a Makefile, which was then compiled by the GCCcompiler into an executable.The original compiler stored a graph as a dynamic array of nodes and an-other of edges, along with the memorised amount of each element. Internally,these arrays were actually more subtle, consisting of an array of the actualelements and a secondary array of indices that contained nothing, or ‘holes’.Focusing on nodes, we term the first array the node array and the second thehole array.In order to iterate through nodes, a program would simply iterate throughall indices between 0 and the largest index holding a node, a value the graphremembers. Each index would have to be checked to ensure it genuinely didhold a node, and was not in fact empty. A pointer could be resolved from theindex and the node could be modified as desired.To delete a node, a program would simply add the given index to the arrayof holes and set the node’s entry in the node array to all zeros. Should the nodehappen to be the last entry in the array, the number of elements in the arraycould instead be decreased rather than add another hole. Future iterationsthrough the node array would then skip over this hole in the array, as theychecked every entry for being a hole or not. This raised performance issues ifa program deleted a large number of nodes, for instance, in a graph reductionalgorithm, as the enormous number of holes would make traversing the finalsmaller graph as slow as the original larger one. A prime example of this isthe most obvious program that recognises discrete graphs by reduction: Main = del!; if node then faildel(x:list) node(x:list)x ⇒ ∅ x ⇒ x Figure 1.1: GP 2 Program is-discrete.gp2
This program should be expected to run in linear time on unmarked, un-rooted graphs, as finding an arbitrary node with no further constraints shouldbe a constant-time operation. Alas, each deleted node adds a new hole at thestart of the node array, making the program actually take quadratic time dueto having to traverse the holes at each rule match. https://github.com/UoYCS-plasma/GP2 .4. BAK’S GP 2 COMPILER . . . . · ,
000 Number of nodes in input E x ec u t i o n t i m e ( m s ) New Impl. (a) New Implementation . . . . · . . · Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) Both Implementations
Figure 1.2: Measured Performance of is-discrete.gp2
When inserting a new node, one could simply pop off the last element of theholes array to give a free index to work within. If the holes array was empty,the largest used index would have to be incremented and the node placed atthe end of the node array. Should the node array be too small, it would bedoubled with the realloc()
C standard library function. The same wouldbe true for the hole array, albeit holes in the hole array would not need to betracked as only the last element is ever accessed. Arrays would never halve insize should they shrink, to avoid needles memory operations. However, simplyreallocating the array raised the possibility of the array changing position inmemory to double in size, making any pointers to nodes held while adding anew node invalid. This meant that Bak was indeed required to store indices tonodes instead of pointers, adding extra memory operations to resolve indicesevery time a node was accessed.To accommodate root nodes, Bak added a linked list of root nodes toeach graph, each entry holding a pointer to a node in the graph’s node array.Nodes would contain a flag themselves detailing if they were a root node ornot. Root node iteration could then simply be done by traversing this list, aconstant time operation should the number of root nodes be constant bounded.Deleting a root node would now require traversing the list as well, to erasethe desired entry from said list. These costs are of course a constant timeoverhead, should the number of root nodes indeed be upper-bounded by aconstant in the compiled program.A node itself would contain a number of entries, including its own index,the number of edges for which it is a target, termed its indegree, the numberof edges for which it is a source, termed its outdegree, its incident edges, label,mark and several boolean flags detailing its nature. These flags were stored inseparate bool entries in the structure, wasting several bytes where only a bitis used.The incident edges to a node were stored in a unique manner, with indices
CHAPTER 1. INTRODUCTION of the first two edges being stored statically as part of the node type itself,and indices of all future incoming and outgoing edges being stored in twodynamically allocated arrays of incoming and outgoing edges, respectively.This would avoid having to allocate arrays for a node should its degree be lessthan three.Each edge would contain its own index, its label, mark, and the indices ofits source and target nodes. It would, similarly to nodes, hold a boolean flagof whether it had been matched or not yet to prevent overlapping matches.In order to parse an incoming graph, Bak implemented a graph parser inBison which would accumulate nodes and edges into a graph as it found themin the input. In order to resolve source and target indices, nodes would bestored in a pre-allocated secondary array, stored at the array index equal tothe ID the node was given in the input file. When an edge was then discovered,resolving its source and target could be done by looking up the indices in thisarray equal to the source and target IDs in the input. Unfortunately, thisopened a large number of issues: for instance, the array was unable to changeits size, meaning only a fixed number of nodes could be entered into the graphbefore attempting to store nodes at unallocated indices, resulting in unhandledsegmentation faults. Also, should a small number of nodes be given enormousIDs, the parsing stage would have to use a huge amount of memory to createan array able to assign at these indices, making this method both incorrectand inefficient should it have been fixed directly.Finally, to accommodate programs running within the condition of an ifstatement, for instance, a stack of states was needed. Bak implemented graphstacks in two varieties: copying the previous graph into a stack to reuse itwhen unwinding, and storing changes to the graph in the stack to undo themin reverse when unwinding. The former simply stored all the data that a nodeor edge contained, reconstructing the node or edge as needed as it unwound.Graph copying would simply perform a deep copy of the graph as expected.
Since Bak completed his PhD, the GP 2 Compiler implementation has notstood still. We consider the state of the codebase at the end of 1st September2015 to be the state of the GP 2 compiler, as described by Bak’s thesis. Thisis represented by the sep-2015 tag on GitHub. Between September 2015and July 2017 there were various minor fixes and changes made by Bak andHristakiev.During the early days of GP 2, a graphical editor was created by Elliot in2013 [42] in C++. Hristakiev worked to revive this implementation in 2015, https://github.com/UoYCS-plasma/GP2-editor .5. GP 2 COMPILER CHANGES to run in the web browser, independently ofthe C compiler implementation [43]. It is likely that there will eventually bea browser-based GP 2 editor in the future, possibly based on Hand’s work.Most notably, the following changes to the language concrete syntax werecompleted before the end of December 2015:1. Replaced the syntax for positioning nodes and edges in rules. Thischange affects the GP 2 Editor only.2. String literals can now contain any printable ASCII character.3. Node and edge identifiers in rules can now start with a digit.4. Host graph node and edge identifiers now have to be integers.During the same period, automatic generation of a Makefile by the GP 2compiler was added, and so also the option to validate a single rule as input,just like validating an entire program. It’s worth noting that the change toallow string literals to contain any printable ASCII character was actuallymistakenly only applied to the program concrete syntax definitions. It wasnot until March 2017 that the grammar for host graphs was updated too.Bak’s last tweak to the GP 2 Compiler implementation was made in July2017. Since then, Atkinson, Campbell and Romo have continued to make bugfixes and corrections, without modifying the intended syntax and semantics.Most notably, in July 2018 Romo added the ability to specify as a compilerargument the number of nodes and edges the compiler’s generated parser setsaside memory for when parsing host graphs, though this change did not actu-ally become part of the official implementation until February 2019. Previousto that, one had to modify the source code of the compiler itself in order toaccept larger input graphs.During August and September 2019, we have updated the compiler im-plementation to address the issues described in Section 1.4. We describe thechanges in detail in Chapter 2. At time of writing, there exist two brancheson GitHub: legacy and master . The legacy branch contains the ori-ginal GP 2 compiler implementation with support for root reflecting morph-isms backported from the new version, which is on the master branch.Finally, it is worth noting that the implementation of the ProbabilisticGP 2 (P-GP 2) [38] Compiler remains separate from the GP 2 Compiler, wasentirely developed by Atkinson, based on the implementation of GP 2 as itstood after Bak finished making modifications in 2017. https://github.com/sdhand/grape https://github.com/UoYCS-plasma/P-GP2 CHAPTER 1. INTRODUCTION hapter 2
Improved Implementation
We now present our modifications to the GP 2 compiler, to overcome theprevious issues highlighted. Our first improvements consisted of fixing graphparsing, abandoning dynamic arrays of nodes in favour of Judy arrays. Ournext and most significant internal change is employing linked lists for nodeand edge iteration, allowing one to jump over deleted elements. In turn,the interface to the compiler was modified to accommodate a range of newoptimisation options, adding flags to toggle internal optimisations for eachcompiled program. We also added a ‘fast shutdown mode’, enabling users tochoose for their program to terminate without freeing memory, and an optionto reflect root nodes in a program if desired. Finally, an integration test suitewas built for more efficient and sound compiler development.
To resolve the issues with parsing, we decided to employ Judy arrays [44],instead of a simple dynamic array. Invented by Doug Baskins, Judy arrays area highly cache-optimised hash table implementation. The size of a Judy arrayis not statically pre-determined and is adjusted, at runtime, to accommodatethe number of keys, which themselves can be integers or strings. Instead ofstoring nodes in the array directly, we also instead store pointers to nodes inthe host graph as Judy arrays can only store references to a single word ofdata. Reallocating the array when doubling it could move the array aroundand invalidate previous pointers, an issue we resolve in the next section. Thisallowed an edge to retrieve pointers to its source and target efficiently dueto Judy arrays’ fast runtime performance [44], [45]. This also resolved prob-lems with unnecessary node array size, allowing node IDs to be arbitrarilylarge without causing memory problems, as the array simply saw these IDs asmeaningless keys in key-value pairs. http://judy.sourceforge.net CHAPTER 2. IMPROVED IMPLEMENTATION
In order to resolve issues with traversing an array with holes, we decidedinstead to employ a linked list of nodes, allowing us to jump over any deletednodes in a single step. We would have new types for entries in the linked list,containing a pointer to the next element and to the current node or edge inquestion. This would allow us to, for instance, run our discrete graph deletionprogram in linear time, as the first node in the list could be accessed in constanttime.A performance issue would soon become apparent: should every list ele-ment, node and edge not be stored in arrays anymore? Each would have tohave a piece of memory allocated for themselves dynamically. Having severalcalls to malloc() for every added node and edge would be highly inefficient,requiring many system calls and memory operations. To resolve this, we de-cided nodes, edges and linked list entries should all be stored in dynamicarrays, doubling in size when too small.At this stage, it became apparent that node indices were largely redundant,exposing internal implementation too much and adding unneeded memoryoperations to resolve a node’s address every time it was accessed. Thus, weset about rewriting the runtime to use pointers to nodes and edges rather thanindices. This in turn added the problem of pointers being invalidated shouldthe array of nodes or edges be moved when realloc() is called to enlargethem. To resolve this problem in turn, we replaced all internal arrays with anew type we dubbed
BigArray s.The
BigArray type, in essence, is an array of pointers to arrays of entries.Each successive array pointed to is double the size of the previous one. The firstentry stores two elements, the second four, and so forth. The
BigArray typeis generic, opting to remember the size of its entries and treating each entryas a chunk of memory rather than deal with actual types of entry. Accessinga given index is constant time, using the position of the largest set bit inthe index to identify which sub-array to access. Only a logarithmic numberof memory allocations are performed overall, with the overall array of arraysbeing reallocated O ( log ( log ( n ))) times - a trivial amount. Moreover, this isan additive value, not multiplicative. BigArray s also contain a static chunk of 160 bytes in themselves, allow-ing for the first few entries in the array to be stored without having to allocatesecondary arrays. When nodes use
BigArray s of their incident edges, thiswould mean avoiding unnecessary memory allocations for nodes of small de-gree, in turn generalising Bak’s solution of storing the first two node indicesstatically with cleaner control logic.
BigArray s also manage holes like the prior implementation did. However,instead of using a second array of holes,
BigArray s instead store a linked list .2. FROM ARRAYS TO LINKED LISTS
BigArray s morememory efficient and making deletion of elements constant time.Most importantly,
BigArray s allow one to allocate more memory to thearray without having to possibly move previous entries in memory, simplycreating a new array. This means the low number of memory allocations maybe maintained without pointers to nodes and edges being invalidated.Thus, three
BigArray s are now stored within a graph, one for nodes, onefor edges, and one for entries in the linked list of nodes, termed
NodeList s.A
NodeList simply contains a pointer to the node it refers to and a pointerto the next entry in the linked list. The same is true of
EdgeList s, albeitfor edges.Each node now contains a
BigArray of linked list entries for edges andpointers to the linked list of outgoing edges and of incoming edges. No iterationthrough edges directly is ever needed beyond printing a graph, which can bedone by iterating through the outgoing edges of every node, so no total list ofedges is maintained.A new issue now presented itself: should a node or edge be deleted, allpointers now represent garbage, the element having possibly been overwrittenby a hole. This shed light on yet another possible optimisation: nodes andedges should remember who references them, and be garbage collected whenthey are referenced by no one. Nodes now retain flags representing if they arein a graph or referred to in the stack of graph changes, and edges rememberif they are in a node’s list of incoming/outgoing edges or in the stack also.Should a node or edge ever be deleted, the operation can be deferred shouldother references still exist. Now, stacks of graph changes can simply storepointers to nodes and edges rather than any data in them, stopping themfrom being truly deleted but simply ignored by the graph that ‘deleted’ them,with no linked list entry pointing to them. This garbage collection saves onmemory in graph stacks and reduces memory operations.In light of these modifications, it became apparent that graph copyingwould be somewhat redundant, as stored pointers would resolve to the originalgraph and not the copied one. We elected to remove graph copying rather thanaugment copying because of this, only supporting undoing changes now. Also,all flags for nodes and edges were condensed into a single char rather thanseparate booleans, to save on memory.2
CHAPTER 2. IMPROVED IMPLEMENTATION
In Section 2.2, we described now the array structure for nodes and edges haschanged. In the previous version of the compiler, there was only a singleedge array associated with each node. In the new compiler, each node has,in addition to an internal
BigArray storing edges, two separate
EdgeList s:one for the outgoing edges, and the other for the incoming edges.It is now possible to run programs that previously required bounded de-gree to obtain a certain worst case time complexity, now with only boundedincoming degree, or bounded outgoing degree, since search plans can now onlyconsider edges of the correct orientation.
It is good practice, in order to aid runtime of analysis of programs for heapmemory bugs, for programs to track and cleanup their allocated heap memory.In particular, when doing this, one must accept a one-time cost when cleaningup the data structures after writing the output graph, during the ‘shutdown’step. In order to mitigate this cost, we have introduced a way to turn this off,so that the generated code will simply exit the process as soon as printing thegraph. The so called ‘fast shutdown’ mode can be enabled with a flag passedto the compiler, as documented in Appendix A.In addition to fast shutdown mode, we have also introduced a more aggress-ive optimisation designed to improve the performance of reduction programs,that will turn off garbage collection of nodes and edges all together, as wellas disabling refcounting of host label lists, making garbage collection of hostlabel lists impossible. Once again, documentation of how to enable this moreaggressive optimisation is provided in Appendix A.
GP 2 theoretical foundation is rooted graph transformation with relabelling.GP 2 rule schemata are used to instantiate genuine ‘rules’ which are thenapplied in the standard way. Unfortunately, Plump and Bak’s model res-ults in derivations not being invertible, even when ignoring application condi-tions. This is because the right square of a derivation need not be a naturalpushout. This has the unfortunate consequence that derivations are not in-vertible. Moreover, that non-roots can be matched against roots, so a rulethat was intended to introduce a new root node, might actually behave like skip . .6. LINK-TIME OPTIMISATION ← → y NPO y PO y ← → Figure 2.1: Example Rooted DerivationCampbell has proposed a new foundation for rooted GT systems with rela-belling [46], that mitigates this problem. Instead of only insisting on matchespreserving root nodes, we also insist on them reflecting them too. This wasformalised by defining rootedness using a partial function into a two-point setrather than pointing graphs with root nodes, thus allowing both squares in aderivation to be natural pushouts, where rules are allowed to have undefinedrootedness in their interface graphs.In order to simulate this new model of rootedness, one only needs to makesmall modifications to the compiler to enforce reflection of rootedness nodesin matches. We have thus made this change on both the legacy and masterbranches of the compiler implementation. For usage details, see Appendix A.
In order to produce faster compiled programs, we have increased GCC’s op-timisation flag from -O2 to -O3 . Moreover, we no longer compile the ‘library’files ahead of time for linking. We compile them with the generated program,with ‘link-time optimisation’ enabled, allowing GCC to more aggressivelyoptimise the whole program.This has lead to a minor change to the CLI interface of the compiler, andalso the generated files. Usage documentation can be found in Appendix A. In order to have confidence in the correctness of both the legacy and master
GP 2 Compiler, we have written various integration tests. As of 20th Septem-ber 2019, there are 140 test cases that are checked. Details of the tests can befound in Appendix B. https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html https://gcc.gnu.org/wiki/LinkTimeOptimization CHAPTER 2. IMPROVED IMPLEMENTATION hapter 3
Timing Results
We conjecture that the new compiler implementation has worst case timecomplexity no worse than the original implementation. We provide runtimeperformances of various programs and graph classes as empirical evidence forour conjecture, where timing results differ only by a constant factor. Welook at the performance of generation programs, reduction programs, and acouple of other programs, including undirected DFS. We show that there aresignificant improvements for some types of reduction programs.Details of the benchmarking software and graph generation can be foundin Appendix C, including which compiler flags were used. (a) Discrete Graph (b) Binary Tree (c) Grid Graph (d) Linked List
Figure 3.1: Input Graph ClassesIn Section 1.4, we observed that even the program that simply deleted allisolated nodes could not be executed in linear time by the old implementation.Somewhat more subtle, is when an input graph is connected, however theprogram splits up the graph as it executes. It is possible to recognise binaryDAGs using a slick reduction program (Figure 3.2) with this property. Onceagain, we observe that the original compiler produces a program that runsin quadratic time on many graph classes, including full binary trees and gridgraphs (Figure 3.1). Our new compiler runs in linear time on such graphs.156
CHAPTER 3. TIMING RESULTS
Main = (init; Reduce!; if flag then break)!; if flag then failReduce = up!; try Delete else set_flagDelete = {del0, del1, del1_d, del21, del21_d, del22, del22_d}init(x:list) up(a,x,y:list) del0(x:list)x ⇒ x x y ⇒ x y a a x ⇒ ∅ del1(a,x,y:list) del1_d(a,x,y:list)x y ⇒ x a x y ⇒ x adel21(a,b,x,y:list) del21_d(a,b,x,y:list)x y ⇒ x ab x y ⇒ x abdel22(a,b,x,y,z:list) del22_d(a,b,x,y,z:list)xy z ⇒ xy
12 12 ab xy z ⇒ xy
12 12 abset_flag(x:list) flag(x:list)x ⇒ x x ⇒ x Figure 3.2: GP 2 Program is-bin-dag.gp2 . . . . . . · . · Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (a) Tree Reduction . . . . · , , ,
000 Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) Grid Reduction
Figure 3.3: Measured Performance of is-bin-dag.gp2
Next, we look at a rooted tree reduction program by Campbell [46] (mod-ified the program to work with unmarked graphs) that was linear time ongraphs of bounded degree in the previous implementation of the compiler.We confirm that it remains linear time in the new compiler, and include gridgraphs as a negative case for the program to determine they are not trees. .1. REDUCTION PERFORMANCE Main = init; Reduce!; Unmark; if Check then failReduce = {prune0, prune1, push}Unmark = try unmark; try unmarkCheck = {two_nodes, has_loop}init(x:list) prune0(a,x,y:list) prune1(a,x,y:list)x ⇒ x x y ⇒ x a x y ⇒ x aunmark(x:list) push(a,x,y:list)x ⇒ x x y ⇒ x y a ahas_loop(a,x:list) two_nodes(x,y:list)x ⇒ x a a x y ⇒ x y Figure 3.4: GP 2 Program is-tree.gp2 . . . . · , ,
500 Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (a) Tree Reduction . . . . · , , ,
000 Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) Grid Reduction
Figure 3.5: Measured Performance of is-tree.gp2
Finally, we look at recognition of Series-Parallel graphs [22], [23], [47]. Wedon’t expect this program to run in linear time on graphs of bounded degree,but we use this as another example, comparing the runtime performance of theoriginal and new compiler implementations. We include grids as an exampleof an input that is not Series-Parallel.8
CHAPTER 3. TIMING RESULTS
Main = {par, seq}!; del; if node then failpar(a,b,x,y:list) seq(a,b,x,y,z:list)x y ⇒ x y ab a x y z ⇒ x z a b adel(a,x,y:list) node(x:list)x y ⇒ ∅ a x ⇒ x Figure 3.6: GP 2 Program is-series-par.gp2 · . . · Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (a) List Reduction . . . . · , , ,
000 Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) Grid Reduction
Figure 3.7: Measured Performance of is-series-par.gp2 (a) Discrete Graph (b) Binary Tree (c) Star Graph (d) Sierpinski Graph
Figure 3.8: Generated Graph ClassesWe don’t expect any complexity improvement for ‘generation’ programs,however we include this class of programs to allow us to verify that this is thecase. Our first test case is the generation of discrete graphs, our second is fullbinary trees, our third is ‘star graphs’, and our final is Sierpinski graphs usingPlump’s program, originally written for GP 1 [48]. .2. GENERATION PERFORMANCE Main = try init then (gen!; del); finishinit(n:int) gen(n,m:int)n ⇒ n n-1 where n > 1 n m ⇒ n m m-1 where m > 1del(n,m:int) finish()n m ⇒ n m ⇒ Figure 3.9: GP 2 Program gen-discrete.gp2
Main = init; (gen!; ret; step!)!; finishinit(n:int) ret(i,j:int) finish(n:int)n ⇒ n 0 j i ⇒ j i n 0 ⇒ n gen(i,n:init) step(i,j,n:int)n i ⇒ n i i+1 i+1 where i < n and outdeg(2) = 0 n i i+1 ⇒ n i i+1 where j < n Figure 3.10: GP 2 Program gen-tree.gp2
Main = {gen1, gen2}!; {fin1, fin2}gen1(n:int) gen2(n:int)n ⇒ n-1 n nwhere n > 1 n ⇒ n-1 n nwhere n > 1fin1() fin2()1 ⇒ ⇒ Figure 3.11: GP 2 Program gen-star.gp2 CHAPTER 3. TIMING RESULTS
Main = init; (inc; expand!)!; cleanupinit(m:int) inc(m,n:int)m ⇒ m:0
10 0 m:n ⇒ m:n+1 where m > nexpand(m,n,p,q:int) cleanup(m,n:int) m:n nq p ⇒ m:n n+1n+1 n+1 q 0 p
01 2 01 201 2 01 2 m:n ⇒ ∅
Figure 3.12: GP 2 Program gen-sierpinski.gp2 . . · , , ,
000 Requested number of nodes E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (a) gen-discrete.gp2
Performance
12 13 14 15 16 17 18 190200400600 Requested tree depth E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) gen-tree.gp2
Performance . . . . · , ,
500 Requested number of nodes E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (c) gen-star.gp2
Performance . . . . · Requested level of recursion E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (d) gen-sierpinski.gp2
Performance
Figure 3.13: Measured Generation Performance .3. OTHER PROGRAM PERFORMANCE We now look at the performance of undirected DFS [26] by looking at a con-nected graph recognition program, and also the performance of a transitiveclosure program. We expect the performance of both the original and newimplementations to be similar for connectedness checking, with the new im-plementation having the edge on grid graphs due to the new implementationof edge lists. In particular, we expect to see good a runtime speedup with thetransitive closure program, which is edge search intensive.
Main = try init then (DFS!; Check)DFS = fwd!; try bck else breakCheck = if match then failinit(x:list) fwd(x,y,n:list)x ⇒ x x y ⇒ x y n nmatch(x,z:list) bck(x,y,n:list)x z ⇒ x z x y ⇒ x y n n Figure 3.14: GP 2 Program is-con.gp2 . . . . · ,
000 Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (a) Performance on Discrete Graphs . . . . · , , , , ,
000 Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) Performance on Grid Graphs
Figure 3.15: Measured Performance of is-con.gp2
Main = link!link(a,b,x,y,z:list)x y z ⇒ x y z a b a bwhere not edge(1,3) Figure 3.16: GP 2 Program trans-closure.gp2 CHAPTER 3. TIMING RESULTS · Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (a) Performance on Linked Lists . . · Number of nodes in input E x ec u t i o n t i m e ( m s ) Old Impl.New Impl. (b) Performance on Grid Graphs
Figure 3.17: Measured Performance of trans-closure.gp2
To begin, it is evident that the new compiler vastly outperforms the old whenexecuting the is-discrete.gp2 program, almost appearing to be constanttime in comparison. We have clearly reached linear complexity, a feat due toour node list’s capacity to skip holes in the underlying node array. The newcompiler also outperforms the old when running trans-closure.gp2 bya significant constant factor. We attribute this to better cache usage; in thelegacy compiler, only the indices of the first two edges are statically stored inthe
Node type, whereas the new compiler stores several more direct pointersto nodes statically in a node’s
BigArray . This not only means the locationsof more edges are loaded into the cache in one cacheline refill, but also theloaded locations can be resolved in one memory operation rather than two,namely checking the edge array at the given index. We consider this evidencethat the new compiler is superior at loading neighbouring edges efficiently.However, the master compiler falls short on some test cases; the programs gen-tree.gp2 , gen-discrete.gp2 , gen-star.gp2 , gen-sierpinsk-i.gp2 , is-tree.gp2 and is-series-par.gp2 run with a worse constanttime factor and the same complexity. Many of these programs only differby a nearly negligible factor though; the only programs that now performsubstantially worse are gen-sierpinski.gp2 , is-series-par.gp2 and is-tree.gp2 . It is possible that gen-sierpinski.gp2 now performs farworse due to the fact that nodes are now accessed in the reverse order theywould have been in the legacy compiler, making the failure somewhat ar-tificial. Many of these failings however are likely due to excessive memoryoperations in the new naive linked list format when iterating through nodes.Some programs performed better or worse depending on the type of inputgraph. is-bin-dag.gp2 saw a boost in performance on linked lists, but adrop on grid graphs. On the contrary, is-con.gp2 was found to be more .4. RESULTS SUMMARY CHAPTER 3. TIMING RESULTS hapter 4
Conclusion
A large number of changes were implemented in the new compiler to improveupon the old version. First and foremost, node and edge lists were addedto improve iteration performance for programs with a high rate of deletion,letting generated node matching code skip large regions of deleted nodes inconstant time.On top of this, input graph parsing was fixed for graphs of arbitrary sizeby using Judy arrays. In the previous implementation, a user had to specifythe maximum number of nodes and edges expected in input host graphs atprogram compile time, with the final generated code always allocating thisamount of memory regardless of the actual size of input graph. In our updatedimplementation, this compile time knowledge is obsolete; we now dynamicallygrow the memory needed during parsing, allowing for arbitrary input graphsize with efficient memory consumption.Also, a method was introduced to shut down the generated program assoon as it has written the output graph without freeing any allocated heapmemory properly. In general, proper management of the heap memory isadvised to avoid bugs due to memory leaks or otherwise. However, one maywish to avoid doing this in the interest of runtime performance, allowing theprocess to terminate as quickly as possible. The newly introduced option givesthe user this choice.Finally, an option was added to require matches to both reflect and preserveroot nodes. By default, GP 2 will allow a non-root in a rule LHS to matchagainst either a non-root or a root in a host graph, but if we insist on matchesreflecting root nodes, matches can only match non-roots against non-roots.With this option employed, rooted rules are now completely reversible andfiner control is gained over rule applications.256
CHAPTER 4. CONCLUSION
Overall, the new master compiler has been shown to maintain previous timecomplexities and improve on the issue of holes in some reduction programs.The is-discrete.gp2 program has been brought down to linear complex-ity, making the new compiler more accurately reflect how complexity of graphprograms should realistically behave. The new compiler also resolves funda-mental issues with the old, such as allowing for arbitrary sizes of input graphsto programs; this makes the compiler fundamentally more correct than it wasbefore.However, a number of programs performed worse by a constant factor dueto the overhead of pointer operations when iterating through a linked list.These issues can be avoided by not using linked lists and iterating througharrays as before, leaving it up to the user to decide how best to compile theirprogram. In any case, no time complexity was worsened. Therefore, we deemthis project a success.
It remains future work to determine if it would make sense to add additionalnode lists for marked vs unmarked to allow a node search to find a marked orunmarked node in constant time. Currently, if one has a graph with arbitrarilymany connected components of arbitrary size, visiting all the nodes in thegraph with an undirected DFS requires quadratic time. If the DFS were tomark all the nodes in the current component, one could then imagine beingable to jump to the next unvisited component in constant time, and thenrepeating until there are no more components. Obviously, maintaining theseadditional data structures requires additional time and space, and in general,will not improve the performance of programs that don’t need to traverse agraph with arbitrarily many connected components.As well as this, investigating worsened performance of programs due tolinked list operations is an issue to resolve. An option is to have each linkedlist entry represent a range of node indices in the underlying node array ratherthan a single node; when iterating through nodes, a list entry may retrievethe array and iterate through it directly within its own range of nodes. Whena node is deleted, the linked list entry containing it then fragments into twoentries, one for the range of nodes before it and one after. This may helpmitigate pointer operations and regain the speed of array iteration while stilljumping over holes in the array.It also remains future work to update the compiler implementation tosupport integers of arbitrary size. The current implementation supports signed32-bit integers only, and overflows are not detected during label or condition .2. FUTURE WORK , and affects both the legacy and master versions ofthe GP 2 Compiler. For example, the following will result in the compiler pro-ducing incorrect code: not (edge(n0, n1) and edge(n1, n0)) . Weare also aware of an issue with try statements within the guard of an if .Finally, ongoing current research is exploring what classes of graph al-gorithms can be implemented in linear time in GP 2 using the current com-piler. Bak showed (undirected) DFS of graphs of bounded degree and boundedcomponents can be performed in linear time [26], [35], and also 2-colouring ofsuch graphs [49]. Due to this report, we know that we can sometimes per-form reduction algorithms that don’t limit the growth of the number of graphcomponents in linear time, and Campbell, Courtehoute and Plump recentlyshowed that GP 2 can recognise trees and topologically sort DAGs of boundeddegree in linear time [39]. https://github.com/UoYCS-plasma/GP2/issues/10 https://github.com/UoYCS-plasma/GP2/issues/28 CHAPTER 4. CONCLUSION ppendix A
Usage Documentation
In this Appendix, we document the usage of the legacy and master branchesof the GP 2 Compiler, as they stand at 20th September 2019. We also takenote of the unofficial Dockerized version of GP2, ‘GP2I’ , and the supportingecosystem. At time of writing, the GP2 Editor has not been updated to usethe master version of the compiler, and should be used with the legacy branch. A.1 Legacy Compiler
The legacy branch specifies the state of the compiler before our modifica-tions. We keep it available so users may see for themselves the differences inthe prior and new compiler versions.Installing the GP 2 legacy compiler may be done with the following se-quence of commands on any standard Linux or Mac computer. If any programsneeded are not installed, install them. git clone -b legacy https://github.com/UoYCS-plasma/GP2.gitcd GP2/Compiler./configuremakesudo make install
A number of flags are available for the GP 2 legacy compiler. In order tovalidate a given program, a user should write gp2 -p
A number of options are available, as can be seen. These are as follows:• -c : Use graph copying instead of undoing on the stack.• -d : Compile with GCC debugging flags, if you should wish to use adebugger on your program.• -m : Compile with root reflecting matches.• -l
The new compiler (on the master branch) may be installed by the followingmeans: git clone https://github.com/UoYCS-plasma/GP2.gitcd GP2/Compiler./configuremakesudo make install
Usage is identical to the legacy version of the compiler. However, someflags have changed:• -c , --max-nodes , and --max-edges have been removed.• -f : Compile a program in fast shutdown mode, to avoid freeing memoryat termination.• -g : Compile a program with minimal garbage collection. This requiresfast shutdown to be enabled.• -n : Compile a program without graph node lists, using arrays instead.• -q : Compile a program quickly, without optimisations.• -l
The purpose of the GP2I is to provide a simple Dockerized interface thatcan be run on almost any AMD64 architecture Linux or MacOS, withoutneeding to install or configure any software, other than Docker. The interfaceis simple, and abstracts away from the fact there is actually a compiler. Onesimply specifies the input program and host graph, and GP2I will compilethe program using GCC 9.2, and execute it on the host graph. The legacy tagged image corresponds to the legacy branch of the GP 2 Compiler, andthe latest tagged image corresponds to the master branch of the GP 2Compiler.Running GP2I is simple. Simply choose the tag , and relative location ofthe GP 2 program prog and input host graph host , as shown below. docker run -v ${PWD}:/data \registry.gitlab.com/yorkcs/batman/gp2i:
It is also possible to specify compiler flags by setting the
GP2_FLAGS environment variable.1. On success, the output graph is written to stdout and the process exitswith code .2. On compilation error, such as due to an invalid program, the details arewritten to stderr , and the process exits with code .3. On program error, such as an invalid host graph or program evaluatingto fail , the details are written to stderr , and the process exits withcode .There also exists ‘GP2I as a service’. That is, we have a gRPC interfaceand server implementation that allows client implementation to execute GP 2programs on host graphs, and receive the program execution time back, alongwith the output graph or an error message. Originally developed in Summer2018, there have been minor modifications since, not least, to support the newGP 2 Compiler. The current interface is provided in Figure A.1. syntax = "proto3"; message Request { string program = 1; string graph = 2; } message Response { oneof payload { string graph = 1; // the output graph string error = 2; // an error message } uint32 time = 3; // execution time } service GP2I { rpc Interpret (Request) returns (Response); } Figure A.1: The gp2i.proto
Interface Description https://grpc.io/ ppendix B Software Testing
In this Appendix, we give an account of the integration tests used to give someconfidence in the correctness of both the legacy and master GP 2 Compiler.As of 20th September 2019, there are 140 test cases that are checked, dividedinto two test suites, quick and slow . The quick suite contains 138 quickrunning tests, designed to test the correctness of the compiler on both small,simple test cases, and small, edge cases. The slow suite contains 2 tests thatare executed on massive input graphs, designed to check the compiler does notfall over when given very large input graphs.The tests execute using the GP2I docker images, as described in SectionA.3. Root reflecting mode (Section 2.5) is used to test the output graphs arecorrect up to isomorphism by means of the generated program in Figure B.1,where $GRAPH is set to the expected graph.We have also setup special debug GP2I images that start the generatedprogram using Valgrind to check for memory leaks and other memory errors,such as control flow that depends on uninitialised memory, on the master version of GP2. Moreover, we run the quick tests using various different com-binations of the compiler flags, although we don’t run all the combinationsthrough Valgrind, since fast shutdown mode (Section 2.4) intentionally doesnot free all allocated heap memory. https://gitlab.com/YorkCS/Batman/Tests http://valgrind.org/ APPENDIX B. SOFTWARE TESTING Main = remove; if {r1, r2, r3, r4} then fail remove() $GRAPH => [ | ] interface={} r1(x: list) [ (n1, x) | ] => [ (n1, x) | ] interface={n1} r2(x: list) [ (n1(R), x) | ] => [ (n1(R), x) | ] interface={n1} r3(x: list) [ (n1, x => [ (n1, x interface={n1} r4(x: list) [ (n1(R), x => [ (n1(R), x interface={n1} Figure B.1: The iso.gp2
Pseudo-Program ppendix C
Benchmarking Details
In this Appendix, we give a quick overview of the benchmarking software usedin this report. Note that all benchmarks were run on a MacBook Pro (Retina,15-inch, Mid 2015) with 2.5 GHz Intel Core i7 and 16GB RAM. We supportboth the new and legacy compilers. For our benchmarking, we set both themax nodes and max edges parameter to 8388608, and when benchmarking thenew compiler, we enabled ‘fast shutdown mode’.The benchmarking software uses gRPC, and in particular, the GP2I serverdescribed in Section A.3. There are two additional gRPC services. A graphgeneration service, and a benchmarking service. The graph generation servicecommunicates with the GP2I service in order to generate graphs using GP 2programs, and the benchmarking service communicates with the graph gen-eration service to generate input graphs, and then with the GP2I service inorder to time the execution of programs on input graphs.There is a benchmarking CLI client that calls the benchmarking servicewith input programs, and graph generation parameters. The service thenstreams back progress information for the client to display, and then, oncebenchmarking is complete, streams the results for the client to display. syntax = "proto3"; message Range { uint32 step = 1; uint32 min = 2; uint32 max = 3; } message Config { string mode = 1; Range range = 2; } Figure C.1: The config.proto
Interface Description356
APPENDIX C. BENCHMARKING DETAILS syntax = "proto3"; import "config.proto"; message Graph { string graph = 1; uint32 nodes = 2; uint32 edges = 3; } service Gen { rpc Generate (Config) returns (stream Graph); } Figure C.2: The gen.proto
Interface Description syntax = "proto3"; import "config.proto"; message Program { string name = 1; string content = 2; } message Setup { repeated Program programs = 1; Config config = 2; uint32 runs = 3; } message Progress { uint32 phase = 1; uint32 total = 2; uint32 complete = 3; } message Benchmark { uint32 nodes = 1; uint32 edges = 2; float rate = 3; float time = 4; } message Benchmarks { string program = 1; repeated Benchmark results = 2; } message BenchStep { oneof payload { Progress progress = 1; Benchmarks benchmarks = 2; string error = 3; } } service Bench { rpc Execute (Setup) returns (stream BenchStep); } Figure C.3: The bench.proto
Interface Description ibliography [1] J. Engelfriet and G. Rozenberg, ‘Node replacement graph grammars,’ in
Handbook of Graph Grammars and Computing by Graph Transforma-tion, Volume 1: Foundations , G. Rozenberg, Ed. World Scientific, 1997,pp. 1–94. doi : .[2] F. Drewes, H.-J. Kreowski and A. Habel, ‘Hyperedge replacement graphgrammars,’ in Handbook of Graph Grammars and Computing by GraphTransformation, Volume 1: Foundations , G. Rozenberg, Ed. World Sci-entific, 1997, pp. 95–162. doi : .[3] A. Corradini, U. Montanari, F. Rossi, H. Ehrig, R. Heckel and M.Löwe, ‘Algebraic approaches to graph transformation. Part I: Basicconcepts and double pushout approach,’ in Handbook of Graph Gram-mars and Computing by Graph Transformation, Volume 1: Founda-tions , G. Rozenberg, Ed. World Scientific, 1997, pp. 163–245. doi : .[4] H. Ehrig, R. Heckel, M. Korff, M. Löwe, L. Ribeiro, A. Wagner and A.Corradini, ‘Algebraic approaches to graph transformation. Part II: Singlepushout approach and comparison with double pushout approach,’ in Handbook of Graph Grammars and Computing by Graph Transforma-tion, Volume 1: Foundations , G. Rozenberg, Ed. World Scientific, 1997,pp. 247–312. doi : .[5] H. Ehrig, M. Pfender and H. Schneider, ‘Graph-grammars: An algebraicapproach,’ in Proc. 14th Annual Symposium on Switching and AutomataTheory (SWAT 1973) , P. Lewis and J. Brzozowski, Eds., IEEE, 1973,pp. 167–180. doi : .[6] H. Ehrig, ‘Introduction to the algebraic theory of graph grammars (a sur-vey),’ in Proc. International Workshop on Graph-Grammars and TheirApplication to Computer Science and Biology , V. Claus, H. Ehrig andG. Rozenberg, Eds., ser. Lecture Notes in Computer Science, vol. 73,Springer, 1979, pp. 1–69. doi : .[7] H. Ehrig, K. Ehrig, U. Prange and G. Taentzer, Fundamentalsof Algebraic Graph Transformation , ser. Monographs in Theoret-ical Computer Science. An EATCS Series. Springer, 2006. doi : . 378 BIBLIOGRAPHY [8] A. Corradini, T. Heindel, F. Hermann and B. König, ‘Sesqui-pushout re-writing,’ in
Proc. Third International Conference on Graph Transforma-tion (ICGT 2006) , A. Corradini, H. Ehrig, U. Montanari, L. Ribeiro andG. Rozenberg, Eds., ser. Lecture Notes in Computer Science, vol. 4178,Springer, 2006, pp. 30–45. doi : .[9] V. Danos, T. Heindel, R. Honorato-Zimmer and S. Stucki, ‘Revers-ible sesqui-pushout rewriting,’ in Proc. 7th International Conferenceon Graph Transformation (ICGT 2014) , H. Giese and B. König, Eds.,ser. Lecture Notes in Computer Science, vol. 8571, Springer, 2014,pp. 161–176. doi : .[10] A. Habel, J. Müller and D. Plump, ‘Double-pushout graph transform-ation revisited,’ Mathematical Structures in Computer Science , vol. 11,no. 5, pp. 637–688, 2001. doi : .[11] O. Runge, C. Ermel and G. Taentzer, ‘AGG 2.0 – new features for spe-cifying and analyzing algebraic graph transformations,’ in Proc. 4th In-ternational Symposium on Applications of Graph Transformations withIndustrial Relevance (AGTIVE 2011) , A. Schürr, D. Varró and G. Varró,Eds., ser. Lecture Notes in Computer Science, vol. 7233, Springer, 2011,pp. 81–88. doi : .[12] M. Hannachi, I. Rodriguez, K. Drira, H. Pomares and E. Saul, ‘GMTE:A tool for graph transformation and exact/inexact graph matching,’ in Proc. 9th IAPR-TC-15 International Workshop on Graph-Based Rep-resentations in Pattern Recognition (GbRPR 2013) , W. Kropatsch,N. Artner, Y. Haxhimusa and X. Jiang, Eds., ser. Lecture Notesin Computer Science, vol. 7877, Springer, 2013, pp. 71–80. doi : .[13] J. Glauert, J. Kennaway and M. Sleep, ‘Dactl: An experimental graphrewriting language,’ in Proc. 4th International Workshop on GraphGrammars and Their Application to Computer Science (1990) , H.Ehrig, H.-J. Kreowski and G. Rozenberg, Eds., ser. Lecture Notesin Computer Science, vol. 532, Springer, 1991, pp. 378–395. doi : .[14] D. Plump, ‘The design of GP 2,’ in Proc. 10th International Workshopon Reduction Strategies in Rewriting and Programming (WRS 2011) ,S. Escobar, Ed., ser. Electronic Proceedings in Theoretical ComputerScience, vol. 82, Open Publishing Association, 2012, pp. 1–16. doi : .[15] A. Agrawal, G. Karsai, S. Neema, F. Shi and A. Vizhanyo, ‘The designof a language for model transformations,’ Software & Systems Modeling ,vol. 5, no. 3, pp. 261–288, 2006. doi : . IBLIOGRAPHY
International Journal on Soft-ware Tools for Technology Transfer , vol. 14, no. 1, pp. 15–40, 2012. doi : .[17] E. Jakumeit, S. Buchwald and M. Kroll, ‘GrGen.NET – the express-ive, convenient and fast graph rewrite system,’ International Journal onSoftware Tools for Technology Transfer , vol. 12, no. 3–4, pp. 263–271,2010. doi : .[18] T. Arendt, E. Biermann, S. Jurack, C. Krause and G. Taentzer, ‘Henshin:Advanced concepts and tools for in-place EMF model transformations,’in Proc. 13th International Conference on Model Driven EngineeringLanguages and Systems (MODELS 2010) , D. Petriu, N. Rouquette andØ. Haugen, Eds., ser. Lecture Notes in Computer Science, vol. 6394,Springer, 2010, pp. 121–135. doi : .[19] A. Schürr, A. Winter and A. Zündorf, ‘The PROGRES approach: Lan-guage and environment,’ in Handbook of Graph Grammars and Comput-ing by Graph Transformation, Volume 2: Applications, Languages andTools , H. Ehrig, G. Engels, H.-J. Kreowski and G. Rozenberg, Eds. WorldScientific, 1999, pp. 551–603. doi : .[20] M. Fernández, H. Kirchner, I. Mackie and B. Pinaud, ‘Visual model-ling of complex systems: Towards an abstract machine for PORGY,’in Proc. 10th Conference on Computability in Europe (CiE 2014) , A.Beckmann, E. Csuhaj-Varjú and K. Meer, Eds., ser. Lecture Notesin Computer Science, vol. 8493, Springer, 2014, pp. 183–193. doi : .[21] A. Habel and D. Plump, ‘Computational completeness of programminglanguages based on graph transformation,’ in Proc. 4th InternationalConference on Foundations of Software Science and Computation Struc-tures (FOSSACS 2001) , F. Honsell and M. Miculan, Eds., ser. LectureNotes in Computer Science, vol. 2030, Springer, 2001, pp. 230–245. doi : .[22] D. Plump, ‘The graph programming language GP,’ in Proc. Third Inter-national Conference on Algebraic Informatics (CAI 2009) , S. Bozapalidisand G. Rahonis, Eds., ser. Lecture Notes in Computer Science, vol. 5725,Springer, 2009, pp. 99–122. doi : .[23] ——, ‘Reasoning about graph programs,’ in Proc. 9th InternationalWorkshop on Computing with Terms and Graphs (TERMGRAPH 2016) ,A. Corradini and H. Zantema, Eds., ser. Electronic Proceedings in The-oretical Computer Science, vol. 225, Open Publishing Association, 2016,pp. 35–44. doi : .0 BIBLIOGRAPHY [24] A. Habel and D. Plump, ‘Relabelling in graph transformation,’ in
Proc.First International Conference on Graph Transformation (ICGT 2002) ,A. Corradini, H. Ehrig, H.-J. Kreowski and G. Rozenberg, Eds., ser. Lec-ture Notes in Computer Science, vol. 2505, Springer, 2002, pp. 135–147. doi : .[25] G. Plotkin, ‘A structural approach to operational semantics,’ TheJournal of Logic and Algebraic Programming , vol. 60–61, pp. 17–139,2004. doi : .[26] C. Bak, ‘GP 2: Efficient implementation of a graph program-ming language,’ Ph.D. dissertation, Department of ComputerScience, University of York, UK, 2015. [Online]. Available: https://etheses.whiterose.ac.uk/12586/ .[27] C. Poskitt and D. Plump, ‘Hoare-style verification of graph programs,’ Fundamenta Informaticae , vol. 118, no. 1–2, pp. 135–175, 2012. doi : .[28] ——, ‘Verifying total correctness of graph programs,’ in Selected Re-vised Papers from the 4th International Workshop on Graph Compu-tation Models (GCM 2012) , R. Echahed, A. Habel and M. Mosbah,Eds., ser. Electronic Communications of the EASST, vol. 61, 2013. doi : .[29] C. Poskitt, ‘Verification of graph programs,’ Ph.D. dissertation, Depart-ment of Computer Science, University of York, UK, 2013. [Online]. Avail-able: https://etheses.whiterose.ac.uk/4700/ .[30] C. Poskitt and D. Plump, ‘Verifying monadic second-order propertiesof graph programs,’ in Proc. 7th International Conference on GraphTransformation (ICGT 2014) , H. Giese and B. König, Eds., ser. Lec-ture Notes in Computer Science, vol. 8571, Springer, 2014, pp. 33–48. doi : .[31] C. A. R. Hoare, ‘An axiomatic basis for computer programming,’ Com-munications of the ACM , vol. 12, no. 10, pp. 576–580, 1969. doi : .[32] G. Manning and D. Plump, ‘The GP programming system,’ in Proc.7th International Workshop on Graph Transformation and Visual Mod-eling Techniques (GT-VMT 2008) , C. Ermel, R. Heckel and J. de Lara,Eds., ser. Electronic Communications of the EASST, vol. 10, 2008. doi : .[33] I. Hristakiev and D. Plump, ‘Checking graph programs for confluence,’in Software Technologies: Applications and Foundations – STAF 2017Collocated Workshops, Revised Selected Papers , M. Seidl and S. Zschaler,Eds., ser. Lecture Notes in Computer Science, vol. 10748, Springer, 2018,pp. 92–108. doi : . IBLIOGRAPHY https://etheses.whiterose.ac.uk/20255/ .[35] C. Bak and D. Plump, ‘Rooted graph programs,’ in
Proc. 7th Inter-national Workshop on Graph Based Tools (GraBaTs 2012) , C. Krauseand B. Westfechtel, Eds., ser. Electronic Communications of the EASST,vol. 54, 2012. doi : .[36] D. Plump, ‘From imperative to rule-based graph programs,’ Journal ofLogical and Algebraic Methods in Programming , vol. 88, pp. 154–173,2017. doi : .[37] T. Atkinson, D. Plump and S. Stepney, ‘Evolving graphs bygraph programming,’ in Proc. 21st European Conference on Ge-netic Programming (EuroGP 2018) , M. Castelli, L. Sekanina, M.Zhang, S. Cagnoni and P. García-Sánchez, Eds., ser. Lecture Notesin Computer Science, vol. 10781, Springer, 2018, pp. 35–51. doi : .[38] ——, ‘Probabilistic graph programs for randomised and evolutionaryalgorithms,’ in Proc. 11th International Conference on Graph Trans-formation (ICGT 2018) , L. Lambers and J. Weber, Eds., ser. LectureNotes in Computer Science, vol. 10887, Springer, 2018, pp. 63–78. doi : .[39] G. Campbell, B. Courtehoute and D. Plump, ‘Linear-time graph al-gorithms in GP 2,’ in Proc. 8th Conference on Algebra and Coalgebrain Computer Science (CALCO 2019) , M. Roggenbach and A. Soko-lova, Eds., ser. Leibniz International Proceedings in Informatics (LIPIcs),vol. 139, Schloss Dagstuhl–Leibniz-Zentrum für Informatik, 2019, 16:1–16:23. doi : .[40] G. Campbell, J. Romo and D. Plump, ‘Fast graphprograms,’ Department of Computer Science, Univer-sity of York, UK, Tech. Rep., 2018. [Online]. Available: https://cdn.gjcampbell.co.uk/2018/Fast-Graph-Programs.pdf .[41] H. Dörr, Efficient Graph Rewriting and its Implementation , ser. Lec-ture Notes in Computer Science. Springer, 1995, vol. 922. doi : .[42] A. Elliot, ‘Towards an integrated development environment for GP 2,’MEng Thesis, Department of Computer Science, University of York,UK, 2013.[43] S. Hand, ‘A graphical editor for GP 2,’ BSc Thesis, Department ofComputer Science, University of York, UK, 2019. [Online]. Available: .2 BIBLIOGRAPHY [44] A. Silverstein, ‘Judy iv shop manual,’ Hewlett-Packard, Tech. Rep., 2002. [Online]. Available: http://judy.sourceforge.net/application/shop_interm.pdf .[45] H. Luan, X. Du, S. Wang, Y. Ni and Q. Chen, ‘J + -tree: A new indexstructure in main memory,’ in Proc. 12th International Conference onDatabase Systems for Advanced Applications (DASFAA 2007) , R. Kot-agiri, P. R. Krishna, M. Mohania and E. Nantajeewarawat, Eds., ser. Lec-ture Notes in Computer Science, vol. 4443, Springer, 2007, pp. 386–397. doi : .[46] G. Campbell, ‘Efficient graph rewriting,’ BSc Thesis, Department ofComputer Science, University of York, UK, 2019. [Online]. Available: https://arxiv.org/abs/1906.05170 .[47] R. J. Duffin, ‘Topology of series-parallel networks,’ Journal of Mathem-atical Analysis and Applications , vol. 10, no. 2, pp. 303–318, 1965. doi : .[48] G. Taentzer, E. Biermann, D. Bisztray, B. Boneva, A. Boronat, L. Gei-ger, R. Geiß, Á. Horvath, O. Kniemeyer, T. Mens, B. Ness, D. Plumpand T. Vajk, ‘Generation of sierpinski triangles: A case study for graphtransformation tools,’ in Proc. Third International Symposium on Ap-plications of Graph Transformations with Industrial Relevance (AGT-IVE 2007) , A. Schürr, M. Nagl and A. Zündorf, Eds., ser. LectureNotes in Computer Science, vol. 5088, Springer, 2008, pp. 514–539. doi : .[49] C. Bak and D. Plump, ‘Compiling graph programs to C,’ in Proc. 9th International Conference on Graph Transformation (ICGT2016) , R. Echahed and M. Minas, Eds., ser. Lecture Notes inComputer Science, vol. 9761, Springer, 2016, pp. 102–117. doi :10.1007/978-3-319-40530-8_7