   [C++] 8 booleans vs. 1 char
  Say, I have a class with 8 booleans. That class is used a lot (it's a base class using about 4 bools, each derived class then can use the other 4). Now, should I go for speed or size?I figured I can do this in 3 ways (not using std::bitset, because its horribly slow):struct One{    bool b[8];};struct Two{    union    {   char c;   struct   {  bool b1 : 1;  bool b2 : 1;  bool b3 : 1;  bool b4 : 1;  bool b5 : 1;  bool b6 : 1;  bool b7 : 1;  bool b8 : 1;   };    };};struct Three{    char c;};Results, if we take method one as 100%Size: - One: 100% (8 bytes) - Two: 12.5% - Three: 12.5%Speed: Set:  - One: 100% (~8.0e-10 seconds)  - Two: 101%  - Three: 107% Check:  - One: 100% (~8.0e-10 seconds)  - Two: 150%  - Three: 150% Unset:  - One: 100% (~8.0e-10 seconds)  - Two: 101%  - Three: 103%So, what is wise...=/I'd prefer method one or three (since two requires bools to be named when base class is initiated, while derived class would need to name the last 4...can method two also be used with an array? where each array element takes one bit?). Two and three do have quite a decrease in stack usage, but reading the bool takes considerably longer!Should I just not bother about memory, since its cheap anyways? Or not bother about speed because its cheap anyways?...Thanks!   
  Go for speed and forget about memory - if you can afford it. How many of these are going to be created at a time?   ;Quote:Original post by toriGo for speed and forget about memory - if you can afford it. How many of these are going to be created at a time?Typically 10 to 1000 (aka. about 7 KiB overhead). Not a big deal, but was thinking of good programming habits and how much overhead one bool actually has...;  I'd go the way of the lesser evil, sacrifice a tiny insignificant bit of memory in favor of speed (100% vs. 12.5% .. that's significant).   ;Quote:Original post by DecriusNot a big deal, but was thinking of good programming habits and how much overhead one bool actually has...Here's a good programming habit:Quote:"The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet."Go with the first method. It is intuitive and easy to understand and will reduce the number of bugs you will have when using your objects.One example of a problem you already have and have not even mentioned or probably noticed. What if all the boolean flags are set to 1? In this case, a binary bit pattern of all 1's would represent a negative value. In this case, it should not be a negative value.Run this code:#include <stdio.h>struct Two{    union    {   char c;   struct   {  bool b1 : 1;  bool b2 : 1;  bool b3 : 1;  bool b4 : 1;  bool b5 : 1;  bool b6 : 1;  bool b7 : 1;  bool b8 : 1;   };    };};int main(int argc, char * argv[]){Two obj;obj.b1 = 1;obj.b2 = 1;obj.b3 = 1;obj.b4 = 1;obj.b5 = 1;obj.b6 = 1;obj.b7 = 1;obj.b8 = 1;printf("%i\n", obj.c);return 0;}Now run this code:#include <stdio.h>struct Two{    union    {   unsigned char c;   struct   {  bool b1 : 1;  bool b2 : 1;  bool b3 : 1;  bool b4 : 1;  bool b5 : 1;  bool b6 : 1;  bool b7 : 1;  bool b8 : 1;   };    };};int main(int argc, char * argv[]){Two obj;obj.b1 = 1;obj.b2 = 1;obj.b3 = 1;obj.b4 = 1;obj.b5 = 1;obj.b6 = 1;obj.b7 = 1;obj.b8 = 1;printf("%i\n", obj.c);return 0;}You should see the first program output -1 and the second output 255. I think we can all agree there is a big difference between the two values. 255 is the correct and the assumed value for your example, not -1. The way you have your code setup now would lead to impossible to tack down bugs in the future and would just be a mess.You might just argue that you can add the 'unsigned' qualifier and be getting the correct results now, but that just goes to show why you should not be trying to do these low level optimizations when you don't understand all of the side effects that can result.These particular issues with signed conversions are a leading cause of exploits in programs. While I don't think you will be worrying so much about those at this stage, you can clearly see now why such programs have bugs. The programmers were hoping to make the program work more efficiently but in turn made it run less safe.That's why I would strongly urge you to let the compiler do the optimizations for you and simply use natural, intuitive designs for your program. [smile]   ;  You'd not worry about micro-optimizing and go with One, because it's the easiest to use and also allows your derived classes to "name" flags to a certain extent by at least providing their own enumerations:struct Base{   enum   { BASE_FLAG_0 = 0, BASE_FLAG_1, BASE_FLAG_2, BASE_FLAG_3, DERIVED_FLAG_0, DERIVED_FLAG_1, DERIVED_FLAG_2, DERIVED_FLAG_3, MAX_FLAGS   };   bool Flags[MAX_FLAGS];};struct Derived{   enum   { MY_FLAG_0 = DERIVED_FLAG_0, MY_FLAG_1 = DERIVED_FLAG_1, MY_FLAG_2 = DERIVED_FLAG_2, MY_FLAG_3 = DERIVED_FLAG_3   };};As Drew said, let the compiler do the optimizations.  If you try and subvert its efforts, very often it will take its revenge when you least expect it :)   ;  No truly correct answer exists for your question given this information. It very much depends on how many instances of this class you will be using and what your usage pattern will be. "Used a lot" could be understood as "access one instance 10 million times" as well as "create 10 million instances that are iterated".Also, there may be other considerations, such as that the union will allow you to collapse 8 clear/set operations into one.If "that class is used a lot" means you may create more instances than will fit into the level-1 (or even level-2) cache, then the smaller variants will generally be faster, in some scenarios possibly 50-100 times.Accessing level-1 cache usually takes about one CPU cycle and can often be coalesced by out of order execution. Fetching from RAM may take upwards of 500 cycles. Testing or setting a bit may be somewhat slower (longer instrucion) than a byte, but usually this isn't nearly as noticeable as cache effects.EDIT:Quote:Typically 10 to 1000 (aka. about 7 KiB overhead)Ah right, that concludes my paragraph about level-2 cache. But then again, maybe not... it will still be fighting over cache with other data   :-)   ;  Thanks for the comments ;-)Quote:Original post by Drew_BentonOne example of a problem you already have and have not even mentioned or probably noticed. What if all the boolean flags are set to 1? In this case, a binary bit pattern of all 1's would represent a negative value. In this case, it should not be a negative value.I must agree I didn't think about making it unsigned, but the thought behind it, was so I can easily set it to 0 or easily compare 2 structs, so it doesn't really matter ;). It's really about the bools, I won't use the value of the char.I do get your point though, it's extra effort which can be error prone. But its also a learning project, so extra effort isn't a big deal.Quote:Original post by Drew_BentonThat's why I would strongly urge you to let the compiler do the optimizations for you and simply use natural, intuitive designs for your program. [smile]^^, good point.   ;Quote:Original post by torisacrifice a tiny insignificant bit of memory in favor of speed (100% vs. 12.5% .. that's significant).You can't compare percentages like that. Instead of 12.5% you could also say 800%.   ;  Yeah, union evilness strikes again!1) is legal and does as you expect.2) is illegal and uses a Microsoft language extension. A unnamed union can not define a type.3) as pointed out does not give you the correct result.