64-bit booleans?

Source claim (that booleans in Rust are 64-bit, for those who don't want to watch the video).

struct Foo {
  b: bool
}

struct Bar {
  b1: bool,
  b2: bool,
  b3: bool
}


fn main() {
  dbg!(std::mem::size_of::<bool>());
  dbg!(std::mem::size_of::<[bool; 3]>());
  dbg!(std::mem::size_of::<Foo>());
  dbg!(std::mem::size_of::<Bar>());
}
$ cargo -q run
[src/main.rs:13:3] std::mem::size_of::<bool>() = 1
[src/main.rs:14:3] std::mem::size_of::<[bool; 3]>() = 3
[src/main.rs:15:3] std::mem::size_of::<Foo>() = 1
[src/main.rs:16:3] std::mem::size_of::<Bar>() = 3

Saying that booleans are 64-bit because of the cache line size sounds weird to me. If one has seven (for instance) booleans stored next to each other, and they are nicely aligned, one would want to be able to fetch all of them in one read.

Are there some weird cases where booleans are instantiated as 64-bit integers in Rust? (I know you can torture a struct into padding so a boolean would have a having 7 bytes of padding next to it, but I don't think that counts).

Back in the later part of 1990 I encountered a system that explicitly defined BOOL as a 32-bit integer, according to the theory that reading out a 16 or 8 bit value was slower than reading out a 32-bit integer on an x86 (but I never saw any benchmarks to substantiate this). I wonder if this sort of thing is where the idea that booleans are implemented as 64-bit integers comes from.

1 Like

Booleans are absolutely not implemented by 64-bit integers in Rust. They always occupy a single, 8-bit byte.

This cannot be changed in any observable way, because types are composable, and the size of the type must be constant. (Otherwise, all sorts of crazy stuff like size_of::<bool>() != size_of::<bool>() would happen.)

11 Likes

Cacheline is an implementation detail of hardwares. They may affect performance but not affect behaviors.

You may grab a can of pringles from local grocery store. You know that the grocery store order pringles' by boxes not each cans, and you also know a pringles factories always export them in fully packed trucks. Does it make sense to say a pringles can is the size of a box or a truck?

18 Likes

Yes, that seems likely. But it is not true today. My understanding is that in general, accessing memory is so much slower than executing CPU instructions that it's very often worth using memory layouts that require more instructions to "unpack" for use, in order to reduce the size of the data stored in memory so that more of it fits in a cache line.

And when the data is in local variables, the compiler is not required to use the exact layout your structs specify; it's very likely the data will be partially or wholly in registers, too, and the optimizer can always use a wider register or stack slot as long as the observable behavior is the same. So using smaller types is not a penalty when main memory is not involved, either.

12 Likes

Well, there's a table on size_of in std::mem - Rust showing that they're definitely 1 byte.

The two things they might have intended to say, though I didn't bother opening the video:

  • Due to padding, adding a bool can make a struct 64 bits bigger because of padding if there's something 8-byte in it already.
  • They're padding them up to 64 bytes because they're worried about False sharing - Wikipedia

"64 bits" and "cache line" just don't make sense together, as cache lines are larger than that on any machine you're plausibly using.

2 Likes

I suspect prime may have misunderstood this video, or perhaps just it's thumbnail: https://youtu.be/IroPQ150F6c

Tldr: the thumbnail has a slide showing a struct with two u32s, a u64 and a bool, with a arrow pointing at the bool saying you only need one bit but you're paying for 64, classic data optimization stuff.

I mostly like prime but he does have these basic brainfarts quite a bit.

4 Likes

Yeah, the initial claim "a boolean is 64 bits" is kinda defensible depending on what you deem important to count, but then he says

a struct of three booleans is 192 bits

Which shows that he's definitely misunderstanding something. No argument based on padding or cache lines will lead to that conclusion.

7 Likes

AFAIK, in Rust, the only 64-bit booleans we have are elements of a Mask<i64, N> type on some targets (such as x86 pre-AVX512), which is highly unusual and not the same as bool. (On other targets we have one bit per element, such as AVX512, so e.g. N = 8 would make the Mask<i64, N> take one byte. none of that is guaranteed, since it's still nightly-only and because we intend Mask to be opaque).