Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Michel Tilman authored and Michel Tilman committed Jun 29, 2019
1 parent 8c962f9 commit 0d819ad
Showing 1 changed file with 3 additions and 3 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down

0 comments on commit 0d819ad

Please sign in to comment.