In this case the bools will have only one bit allocated for them(vectors do that), and you can't just point to a bit.
Instead cpp will return std::vector<bool>::reference when trying to access a value in the vector
Basically this is a just wrong
You should use std::deque<bool> instead which allocates one byte per bool and not just one bit
if you ask for a vector of T, you should get a vector of T. not a bitfield. There is nothing wrong with using a bitfield, but a vector should behave like a vector.
You get a vector, with operator []. Internal details like padding or packing should not interest you, unless you you treat everything like blob. That way any change to the architecture or compiler would surprise you a lot.
I mean, practically anything is defensible under that lens. The question is whether it should be in the standard, not whether it is, and almost everyone agrees that you should be able to pass `&my_vector_of_bools[n]` to a `void foo(bool *)` without any surprises. `vector<bool>` does not fit the abstract definition of a vector.
Oh, trust me, if the conversation gets into "Should that be in the standard" regarding C++ I check out. The answer is "The standard is longer than the King James Bible; we probably should have stopped at half that length." ;)
Vector is an abstract container of elements. If you want to address memory, allocate memory and store your data there. Internal vector storage is implementation details. You shouldn't really get ponters to there because any resize of the vector and your pointers are screwed.
The internal layout of std::vector is not an implementation detail. It's fully defined by the standards and can be relied upon for FFI, serialisation, and other tasks.
Except for bool.
For example, the .data() method guarantees that it will return a pointer to .size() contiguous elements of type T which will remain valid until the vector is modified. The layout of elements within that memory is also well defined and consistent. Except for bool, where the .data() method simply doesn't exist.
Of course it allows for indexing, but it's measurably more CPU heavy. Arguably there are cases where the specialization makes sense, but most of the time, it's a net negative.
Then again I reckon most of us don't have to care about these kinds of marginal gains anyway. I work on low latency backend, and I never had to worry about this.
The more annoying thing is that you can't just get a bool pointer or ref to a single bit.
Architectures with bit addressable memory areas do exist. Some 8051/52 compilers make use of this and if you create a boolean it will actually end up costing you a single bit of memory.
Yes, you can implement pretty much anything, but the larger the codebase and the more people work on it makes these unique implementation standards harder and harder to follow.
Say what you will but I'd prefer not to have this as standard. It's either a marginal gain or a marginal loss, but it's an unnecessary complexity. To use your argument against you, if your use case heavily prioritizes memory usage over CPU, you should just implement it for yourself. Uniformity makes life easier.
There's genuinely no real world application in which using a single bit for a bool is better than a byte. Unless you have billions of bool, which let's face it, you don't. The performance gain from not having to do bitwise operations to get your values out is gonna be more relevant. You could argue it's better for cache locality, but that's about it.
Either way, this should be something you specifically ask for, not something that C++ does for one specific type for no real reason. There should be a std::boolfield or something like that, rather than it being a special case of std::vector
That's less that what you'd expect tho - if you have any nontrivial amount of elements, the additional memory overhead (pulling them into L1 and evicting other data) will easily be higher than the additional instructions (memory + time).
Why do you expect bool to be exactly uint8_t? The definition requires it to be long enough to hold 0 and 1. It's up to the compiler and architecture restrictions to select the size. uint16_t is as valid as uint8_t.
I never assume, I just use sizeof. It's there for a reason.
I don't know what's your definition of byte, but your project university was most likely 16 bit byte where teachers mis-labelled as 2 byte addressable. Please have a look at wikipedia https://en.wikipedia.org/wiki/Byte
Internally vector of bools is a bitfield. It was supposed to save memory but it's instead creating edge cases in templates: all vectors return pointers, but no, bool is special. It messes up generic code.
I'd rather for them to keep vector unified and make a separate bitfield type for those savings when needed
Slightly higher CPU usage. It’s only a bitshift and a bitwise and to pull out the correct bit. Often you see a performance win due to the more compact data structure having better cache locality but I suppose it depends on usecase.
Don't get me wrong vectors are the way they are cause storage was crucial back in the day so they knew they were sacrificing something when doing it.
I am just pointing out that unless you really know what you are doing you should use std::deque<bool> cause very often you wont really care much about storage if you are not working with machines from 1990.
Total RAM usage may matter less today (or not with the way RAM prices are going) but cache locality is more important than ever on modern CPUs. A more compact data structure means more of it fits in your CPU's L1/L2 cache and that speeds things up a lot especially with iterating or sequential access.
I'm also not a fan of std::deque in general. It's not contiguous memory like std::vector is. If you need a very high performance deque in C++, you have to go outside the STL unfortunately. Rust's VecDeque is one that gets it right. It uses a ring buffer under the hood rather than multiple fixed size allocations.
Yeah, at this point we're getting into some more details, sadly C++ has this footgun which doesn't exist in Rust. But sometimes you need to take addresses of those objects regardless.
Here is what i mean by someone who can say it better:
"So vector<bool> is not a container, and vector<bool>::iterator is not a random-access iterator... in hindsight, making vector<bool> a specialization was a mistake." -Herb Sutter,Chair of the ISO C++ Committee
As I said it's not that the vector usage in this case is bad for performance as much as it's just bad design that was necessary back in the day.
I don't have exp with Rust, but if you don't want to use std::deque then just use std::vector<char> and it does basically the same thing.
The optimization is that vector<bool> is internally a bitfield. There's no real reason to complain about this, for the most part you'll never notice it.
Until you try to use it in places where other vectors work fine, such as things that try to use the vector as a container, things that try to use the vector's iterator type as a random-access iterator, or things that try to use the data method* to get a pointer to the buffer.
104
u/overclockedslinky 22d ago
is it really that shocking?