diff --git a/README.md b/README.md index 550cf9b..df7597d 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ Note that both the Pharo and Scala implementations assumed a regular 9-by-9 grid ## Pure Swift implementations -The *ClassyDancingLinks* algorithm also uses classes to represent the nodes of the Dancing Links grid. The grid is a sparse implementation of a constraint matrix, where the elements (nodes) are connected to other nodes in the same row and column by means of doubly-linked lists. Once the grid is set up, the bulk of the computation of the Dancing Links algorithm consists in unlinking and relinking nodes from and to the lists, resulting in constant updates of object references. Since ARC does not deal well with cycles in the context of this algorithm, the choice was made to not use weak references (for instance, tests actually indicated a substantial performance loss when using weak references). Instead, the algorithm keeps track of the nodes in the grid using strong references, and explicitly releases the grid nodes at the end. This implementation takes about 7.6 ms for the included evil sudoku performance test case. +The *ClassyDancingLinks* algorithm also uses classes to represent the nodes of the Dancing Links grid. The grid is a sparse implementation of a constraint matrix, where the elements (nodes) are connected to other nodes in the same row and column by means of doubly-linked lists. Once the grid is set up, the bulk of the computation of the Dancing Links algorithm consists in unlinking and relinking nodes from and to the lists, resulting in constant updates of object references. Since ARC does not deal well with cycles in the context of this algorithm, the choice was made to not use weak references (some tests actually indicated a substantial performance loss when using weak references). Instead, the algorithm keeps track of the nodes in the grid using strong references, and explicitly releases the grid nodes at the end. This implementation takes about 7.5 ms for the included evil sudoku performance test case. -The *StructuredDancingLinks* algorithm is struct-based, and, sort of implements its own memory management. A *node store* manages the links between the struct nodes (links are just indices in the node store array). This algorithm also foregoes simple iterator abstractions to loop over the doubly-linked lists. This algorithm is significantly faster than *ClassyDancingLinks*, requiring about 1.2 ms to find the evil sudoku solution. (An experimental non-recursive implementation of this algorithm reduces the required time even further to about 1 ms or less.) +The *StructuredDancingLinks* algorithm is struct-based, and sort of implements its own memory management. A *node store* manages the links between the struct nodes (links are just indices in the node store array). This algorithm also foregoes simple iterator abstractions to loop over the doubly-linked lists. This algorithm is significantly faster than *ClassyDancingLinks*, requiring about 1.2 ms to find the evil sudoku solution. (An experimental non-recursive implementation of this algorithm reduces this time even further to about 1 ms or less.) -Both benchmarks measure the performance of the respective algorithms to find the solution for the evil sudoku. Each solution is tested for *correctness*: does the solution comply with the rules of a valid sudoku? This validation is taken care of by the sudoku initializer. Whether the solution is also *complete* (i.e. have all empty cells in the sudoku been assigned a number), is not handled by default. When we add this completeness test to the benchmarks, the results of both algorithm change spectacularly (for reasons as yet unclear). Perfomance of the class-based implementation drops to about 10 ms, whereas the struct-based implementation suddenly requires 35 ms. +Both benchmarks measure the performance of the respective algorithms to find the solution for the evil sudoku. Each solution is tested for *correctness*: does the solution comply with the rules of a valid sudoku? This validation is taken care of by the sudoku initializer. Whether the solution is also *complete* (have all empty cells in the sudoku been assigned a number), is not handled by default. When we add this completeness test to the benchmarks, the results of both algorithms change spectacularly (for reasons as yet unclear). Perfomance of the class-based implementation drops to about 10 ms, whereas the struct-based implementation suddenly requires 35 ms. Implementing a version of the algorithm in Swift that approximates the performance of the Scala solution clearly turned out to be less straightforward than expected.