Carl

Carl Mastrangelo

A programming and hobby blog.


Storing Lists in Registers

While working on my Wordle solver, I found a trick for treating integers as little lists. To keep memory usage down, I decided to pack the 5 letter words into a 32 bit integer. I use 6 bits per letter, leaving the top bit as a zero. This allows doing some SIMD tricks without having to expand the nibbles later.

However, sometimes it’s convenient to treat each Wordle word as a variable length list of characters, rather than the fixed size, 5 letter words that the game uses. Since the game uses up to 5 letters per word, and each letter consumes a 6 bit nibble, that leaves 32 - (5 * 6) = 2 bits left for use. 2 bits isn’t enough to encode a length field. However, we can use a trick similar to NULL terminated C strings: 1-bit prefixed ints.

This trick works by treating each 32 bit int as a [0, 5] length string of 6 bit values, prefixed by a 2 bit prefix. For example:

bits:  01EE EEEE DDDD DDCC CCCC BBBB BBAA AAAA
index: 1098 7654 3210 9876 5432 1098 7654 3210

The A-E bits represent the nibbles representing the letters, while the 01 prefix indicates the end of the string. To detect the length of the String, we count the number of leading 0 bits and divide by 6 to get the length:

MAX_CODES_PER_WORD = 5;
BITS_PER_CODE = 6;

int zeros = Integer.numberOfLeadingZeros(codeWord);

return MAX_CODES_PER_WORD - ((zeros - 1) / BITS_PER_CODE);

This is nice because Integer.numberOfLeadingZeros() is a single instruction on Intel architectures. Additionally, while dividing by 6 would normally be slow, the compiler optimizes this to a “magic number” division, i.e. a multiplication by (2^32) / 6. On my machine, calculating the length is around 3ns.


Home

You can find me on Twitter @CarlMastrangelo