Relax. Step back. Try to divine the true nature of the problem. The object is not to count neighbors and check cell states as quickly as possible; that’s just one possible implementation. The object is to determine when a cell’s state must be changed and to change it appropriately, and that’s what we need to do as quickly as possible.
— M. Abrash, Graphics Programmer’s Black Book, p. 339

It’s quite interesting to read the rest of the explanation so I won’t reproduce it here. We’re going to copy his result, though, and use part of each byte in our cell array to include the neighbour count.

One interesting point to note is that in his version of ClearCell, to clear the lowest bit he uses

cells[cellIndex] &= ~0x01;

but this won’t work in C#. Instead you need to do this

cells[cellIndex] &= 0xFE;

Apart from that, the rest of the algorithm moves over easily.

image

After we’re finished, there’s not really any non-WPF code worth optimizing further. (The only one that appears in the “Functions Doing Most Individual Work” list is CellMap.NextGeneration() at 2%)

I’m also going to take this opportunity to do a bit of refactoring before moving on (e.g. moving the initialization code into CellMap itself).

(Code so far)