2

Disclaimer: Please correct me in the event that I make any false claims in this post.

Consider a struct that contains eight bool member variables.

/*
 * Struct uses one byte for each flag.
 */
struct WithBools
{
  bool f0 = true;
  bool f1 = true;
  bool f2 = true;
  bool f3 = true;
  bool f4 = true;
  bool f5 = true;
  bool f6 = true;
  bool f7 = true;
};

The space allocated to each variable is a byte in length, which seems like a waste if the variables are used solely as flags. One solution to reduce this wasted space, as far as the variables are concerned, is to encapsulate the eight flags into a single member variable of unsigned char.

/*
 * Struct uses a single byte for eight flags; retrieval and
 * manipulation of data is achieved through accessor functions.
 */
struct WithoutBools
{
  unsigned char getFlag(unsigned index)
  {
    return flags & (1 << (index % 8));
  }
  void toggleFlag(unsigned index)
  {
    flags ^= (1 << (index % 8));
  }

private:
  unsigned char flags = 0xFF;
};

The flags are retrieved and manipulated via. bitwise operators, and the struct provides an interface for the user to retrieve and manipulate the flags. While flag sizes have been reduced, we now have the two additional methods that add to the size of the struct. I do not know how to benchmark this difference, therefore I could not be certain of any fluctuation between the above structs.

My questions are:

1) Would the difference in space between these two structs be negligible?

2) Generally, is this approach of "optimising" a collection of bools by compacting them into a single byte a good idea? Either in an embedded systems context or otherwise.

3) Would a C++ compiler make such an optimisation that compacts a collection of bools wherever possible and appropriate.

manderc3
  • 79
  • 3
  • 1
    It might interest you to know that [`std::vector` is specialized on `bool`](http://en.cppreference.com/w/cpp/container/vector_bool) to do just this sort of thing. – Fred Larson Jan 16 '18 at 22:28
  • 4
    The more obvious way to handle this (assuming there is a need for it) would be to [use bitfields](https://stackoverflow.com/questions/308364/c-bitfield-packing-with-bools), which can use the `bool` datatype, while letting the compiler handle the shifts and masks for you. `std::vector` is a generalization of this for an arbitrary number of fields (but it has some quirks that make it problematic as a drop-in replacement for arbitrary collection types). – ShadowRanger Jan 16 '18 at 22:29
  • 4
    This is a speed/space tradeoff. It may or may not be an optimization, depending on your requirements. – Pete Becker Jan 16 '18 at 22:32
  • There's also [`std::bitset`](http://en.cppreference.com/w/cpp/utility/bitset). – Fred Larson Jan 16 '18 at 22:32

2 Answers2

1

Would the difference in space between these two structs be negligible?

That depends on how many values you are storing and how much space you have to store them in. The size difference is 1 to 8.

Generally, is this approach of "optimising" a collection of bools by compacting them into a single byte a good idea? Either in an embedded systems context or otherwise.

Again, it depends on how many values and how much space. Also note that dealing with bits instead of bytes increases code size and execution time.

Many embedded systems have relatively little RAM and plenty of Flash. Code is stored in Flash, so the increased code size can be ignored, and the saved memory could be important on small RAM systems.

Would a C++ compiler make such an optimisation that compacts a collection of bools wherever possible and appropriate.

Hypothetically it could. I would consider that an aggressive space optimization, at the expense of execution time.

STL has a specialization for vector<bool> that I frequently avoid for performance reasons - vector<char> is much faster.

Sid S
  • 6,037
  • 2
  • 18
  • 24
1

we now have the two additional methods that add to the size of the struct

Methods are code and do not increase the size of the struct. Only data makes up size on the structure.

3) Would a C++ compiler make such an optimisation that compacts a collection of bools wherever possible and appropriate.

That is a sound resounding no. The compiler is not allowed to change data types.

1) Would the difference in space between these two structs be negligible?

No, there definitely is a size difference between the two approaches.

2) Generally, is this approach of "optimising" a collection of bools by compacting them into a single byte a good idea? Either in an embedded systems context or otherwise.

Generally yes, the idiomatic way to model flags is with bit-wise manipulation inside an unsigned integer. Depending on the number of flags needed you can use std::uint8_t, std::uint16_t and so on.

However the most common way to model this is not via index as you've done, but via masks.

bolov
  • 72,283
  • 15
  • 145
  • 224