Our Performance is massiv: Getting the Most Out of Your Hardware in Haskell —

17 Likes

This is fantastic. I was looking into this library and wondering about the particular scenarios where it’s better than nested vectors. Perfect timing

Is there a reason why you prefer to use Word8 with COMPLETE pattern synonyms instead of an ADT (Dead | Alive) with a Storable instance? If both representations are equally fast, I would prefer the ADT (but they may not be).

Good question! I’m not involved at all in this work, though; I’m just reposting an interesting blog post.

The article does say this:

We use a Word8 to represent the CellState here, as this allows us to use the more efficient representation of both vector Vectors and massiv Arrays. We could have defined a sum type, or used Bool as our newtype representation instead, but in both cases, this would have meant using boxed representations, which take more space and are less compact in memory[1]. However, this introduces a lot of unnecessary invalid states; we avoid this problem by defining a pair of pattern synonyms, along with a COMPLETE pragma to ensure that we get an exhaustive match once we match on both of them.

Another reason behind our choice of Word8 has to do with our need to determine the number of living cells in a neighbourhood quickly. With Word8, we can directly sum over representations, as living cells are 1, while dead ones are 0. If we had used a sum type (or Bool), this would involve an additional conversion, whereas in our case, a coerce is all we need.

I think the first paragraph does overlook the possibility of using storable vectors, but the second point is a good reason to use Word8 over an ADT.

Edit: although, in your storable instance you could of course map Alive → 1 and Dead → 0, which should allow you to do a similar trick for adding up live cells. Then you would have to do some custom peeking which may not be well supported by massiv and vector.

4 Likes