Expand description
Bit level kernels: counting, searching, combining and packed fields.
Redis calls a string used this way a bitmap, and it is not a separate type:
SETBIT, BITCOUNT, BITOP and BITFIELD all work on the ordinary string
a SET would have left behind, which is why this file is kernels over byte
slices and nothing else. The keyspace side, which is where a key turns into
bytes and where growing a value is decided, is in
bitmaps.
§Which end a bit is
Bit zero is the top bit of byte zero. That is the convention every one of
these commands uses and it is the opposite of the one a language’s shift
operators suggest, so it is worth being blunt about it: bit i is
bytes[i / 8] & (0x80 >> (i % 8))It falls out of wanting BITPOS over a bitmap of user ids to answer in the
order the ids were assigned, and it is why a u64 loaded out of the middle
of a bitmap has to be read big endian for u64::leading_zeros to mean the
distance to the next set bit.
§Counting
count is four u64 accumulators fed by count_ones. That is the same
shape as Redis’s redisPopcount, which unrolls by four for the same reason:
popcnt has a three cycle latency and one per cycle throughput on every x86
since Nehalem, so a loop with one accumulator is latency bound at a third of
the rate and four independent chains fill the pipe. On aarch64 there is no
scalar popcount at all and LLVM turns the same loop into cnt over a vector
register plus a widening add tree, which is why this is written as an
ordinary loop rather than as intrinsics: the ordinary loop is what both
backends already do well, and an intrinsic version would be two more code
paths to keep right for no measured gain.
§Combining
combine does BITOP, including the four operations Redis 8.2 added:
DIFF, DIFF1, ANDOR and ONE. It works a block at a time over a fixed
stack buffer rather than allocating one accumulator per source, so a BITOP
over eight sources touches the same two kibibytes of stack whatever the
bitmaps weigh, and each block of each source is read once while it is warm.
The alternative, folding whole bitmaps one source at a time, walks the
destination once per source and that is where a BITOP over big bitmaps
spends its time.
Structs§
- Field
- One
BITFIELDfield type:u8,i37and so on.
Enums§
Functions§
- adding
- The value an
INCRBYofbyshould write, orNoneforFAIL. - combine
- Run
opoversrcs, fillingout. - count
- How many bits are set.
- count_
range - How many bits are set in the half open bit range
from..to. - find
- The first bit equal to
setin the half open bit rangefrom..to. - get
- The field of
fbits at bitat, reading past the end as zeros. - set
- Write
valinto the field offbits at bitat. - setting
- The value a
SETofvalshould write, orNoneforFAIL. - width
- How long the result of
opoversrcswill be.