Skip to main content
GameDev.net gamedev.net
🔒 Locked

Bit-fields vs. unions

Started by Russell Jun 18, 2002 at 3:09 PM 17 replies 350+ views
Original Post
Russell
Russell
I''m curious if using bit fields or using unions would be more efficient for the following program. I''m writing a class for a chess piece, and I''d like to keep the internal representation as small as possible (well, at least 32-bits or less). Originally I was using two ints, and doing &''s and >>''s to extract the data, but then it seemed more reasonable to use bit fields or unions. Now, if I use bit fields, then the compiler will probably still use some &''s and >>''s, but it will be able to create the most efficient code for doing that. The union on the other hand shouldn''t require any &''s and >>''s, but it should require a single array lookup (computing offset). I''ll provide a little example of what I''m talking about.
  
class Piece {
  public:
    // methods here...

  private:
    unsigned : 6 _index;
    unsigned : 1 _color;
    unsigned : 4 _type;
    unsigned : 1 _alive;
};

// Or, here''s the union example...


union Piece {
  public:
    // methods here...

  private:
    unsigned int  _data;
    unsigned char _values[4];
};
  
In the class, you would access the data by doing simple _index = whatever;, and so on, but in the union, you would have to do something like _values[INDEX] = whatever;. I also will need to be able to quickly copy over a piece''s data, which would mean the union would be a nice choice, and then I could just copy the Piece''s _data field over if I needed to, and comparing pieces woud be simple too. So which will work more efficiently? Or are they all equally fast? Thanks, Russell
Russell
Russell
Ah...so that''s what those anonymouse things are for I knew there was some reason those existed. That does seem like a much nicer way to handle that. Thanks!

Russell
Stoffel
Stoffel
Those two examples are also doing completely different things. FYI, the code for bit fields is incorrect. Here''s the correct way (and a change I made):

  
class Piece {
public:
// methods here...

private:
char _index : 6 ;
char _color : 1 ;
char _type : 4 ;
char _alive : 1 ;
};

Notice I changed your unsigned to char. The size of this structure is 2 bytes, whereas the union would have a size of 4 if you used 4 chars. The reason is that bit fields will expand to the largest size you give them (unsigned int in your case, the int was implicit).

So you save 2 bytes per object here, but accessing these members is probably going to be slower than accessing members that exist on byte boundaries, which is what you get from the union method. Unless your compiler has some wiz-bang optimizations I don''t know about (always possible), each bit-field access will need some masking and/or shifting that isn''t needed for the union case.

Unless you''re worried about transferring in and out of some media (byte stream, file format, etc.) where the above format is already set in stone, or you''re memory limited, I''d go with unions for efficiency''s sake.
Shannon Barber
Shannon Barber
Wouldn''t using int will yield the best performance on all systems? (it''s sized to make the CPU happy, oui?)

If you have many data elements, the first one will probably be faster because you''ll move less memory between the CPU & system RAM.
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Stoffel
Stoffel
Yeah, probably it would. I fear going that far might be getting into pre-optimizing. Some do agree with the "use int everywhere" philosophy (floating point needs notwithstanding), but it''s not popular where I work. I usually stick with the "lowest reasonable size" method, if only out of habit
Dean Harding
Dean Harding
If you''re just writing a chess game, why is the size of the data so important? I mean there''s only going to be 32 pieces, so even on a PDA or something, using 4 ints isn''t going to hurt (32 * 4 = 128 bytes...) And on a 32-bit architecture, accessing data on 32-bit boundaries is much faster than on 8-bit boundaries...



codeka.com - Just click it.
Russell
Russell
Well, here''s the deal. I will need to be backing these things up. For example, when a piece gets captured, or each time a move is made, you have to backup the castling rights, 50 move rule counter, en passant square, and maybe some other things I''m not recalling of the top of my head.

So if I have a Piece data structure that is 32-bits total, then I can back that up quicker than if I had four 32-bit values, right?

So the decision is between accessing part of the 32-bit structure as 8-bit values, as in the union example, or having to backup four 32-bit values. So which is preferable?

Another idea I haven''t tinkered with yet is to store pointers to things instead of backing them up. For example, I will have a piece list along with the square array, and I could store which piece was captured by storing a pointer to it and setting it''s ''alive'' field to 0 or false. There''s no reason castling rights should go over 32-bits, 50 move rule counter is just an int anyway, and the en passant square is an index, so it''s an int too. So as long as I can take care of the captured piece by use of a pointer, I don''t need to worry about the size of the piece structure, right?

The other thing I was wondering about is whether or not I need to have a union. The reason I was using a union is because I wanted to have access to a 32-bit value that contained all of the piece info, and that allowed me to simply copy over the data and back it up quick. But if I recall correctly, a class would act the same way using default assignment which would do a bit by bit copy, right? So I don''t need to worry about using a union anyway, right?

Anyway, thanks for your help and hopefully I''ll get this all worked out.

Russell
Cybertron
Cybertron
You shouldn''t try to save memory by shoving variables into a bitfield

Today ram is dirt-chep, reading DWORDs is faster too. Bitfields are good for HUGE arrays/files and for slow networks
Inmate2993
Inmate2993
Yeah, unless this is a PDA or something, you shouldn''t worry so much about how wide the data is for a small set of pieces. Since you have 4 pieces of info per piece and using a least privaledge class structure, having 4 chars is good enough. If you want to really record every move made then, have the piece class have a member function that will pack the 4 chars into a single thing and then save that. A good thing to remember is that the upfront and important data should be as easily accessable as possible, stashing them in union''ed bit fields just makes coding the actual moving and rules analysis process that more difficult.

-> Will Bubel
-> Machine wash cold, tumble dry.
william bubel
Russell
Russell
I''m not worried about memory usage. My concern is that if I have a data structure that is 4 DWORD''s in size, that will take 4 times as long to move around.

So the question is which will end up being the most efficient in the end?

-Using a data structure that is 32-bits total, and using potentially slower (maybe) accessing of each 8-bit value? Ex.

union Thing {
// ...
unsigned int data;
struct {
unsigned char value1;
unsigned char value2;
unsigned char value3;
unsigned char value4;
};
};

OR...

-Using a data structure that is 4 DWORDS in size, which will allow for quicker (maybe) data access via 32-bit quantities, but potentially slower copying of 4 DWORDS vs. 1 DWORD. Ex.

class Thing {
public:
// ...
unsigned int value1;
unsigned int value2;
unsigned int value3;
unsigned int value4;
};

I''m only concerned with speed. I am only having trouble deciding between the copying operations and the accessing operations, and which will be the most efficient overall.

Russell
Cybertron
Cybertron
Using chess pieces as an example, you shouldn''t need to move any pieces. Each piece should have an X and Y position on the grid, rather than moving them around in a 2D array

The CPU also has a 32-bit architecture, so copying 4 bytes is way faster than just one. Memory is also very fast, harly anyone uses ram slower than PC133. Assuming the data bus is 32-bits (it probably is 64-bit or higher) thats 532 mb/s. Moving or even reading/writing chess pieces is no problem!

Also packing bits or bytes uses more CPU power than simple memory acces

You should focus your optimization efforts elsewhere, memory isn''t a bottleneck in this case
Dean Harding
Dean Harding
quote:
Original post by Russell
I''m not worried about memory usage. My concern is that if I have a data structure that is 4 DWORD''s in size, that will take 4 times as long to move around.



If this is a chess game, then moving 128 bytes every move is going to be totally insignificant compared to the time your AI will spend calculating it''s next move. Even if you''re not planning on having an AI (i.e. only two-player games supported) then the time a player spends thinging about his move is going to be even longer!


codeka.com - Just click it.
Russell
Russell
quote:
Original post by Cybertron
Using chess pieces as an example, you shouldn''t need to move any pieces. Each piece should have an X and Y position on the grid, rather than moving them around in a 2D array

The CPU also has a 32-bit architecture, so copying 4 bytes is way faster than just one. Memory is also very fast, harly anyone uses ram slower than PC133. Assuming the data bus is 32-bits (it probably is 64-bit or higher) thats 532 mb/s. Moving or even reading/writing chess pieces is no problem!

Also packing bits or bytes uses more CPU power than simple memory acces

You should focus your optimization efforts elsewhere, memory isn''t a bottleneck in this case


In my program, I''m using a board (128 square array, 0x88 style), and a piece list as well. For the AI to be fast, you need both I think. For example, when generating legal moves for a pawn, you need to know if there is a piece in front of the pawn. Using only a piece list with X and Y coordinates (actually I only use indexes), you would have to loop through the piece list searching for a piece with the appropriate coordinates. Since I have both, I can just do a simple add of the offset for 1 rank forward, and see if it''s empty. If it''s empty, the pawn can move. Similar things can be done for determining if a square is attacked by a certain piece, which is useful for detecting checks and evaluation. So the square array is necessary. The piece list is needed because you don''t want to have to search through 64 squares for the pieces each time you have to generate the legal moves. So you just loop through the piece list and get it''s index and then generate legal moves for that piece on that index. So you do a maximum of 16 iterations vs. maximum of 64. So both are necessary.

And just to clarify one last time, I should not worry about using a small structure and just use 32-bit values for what I need? So instead of using 4 bytes (totalling 32-bits) for a piece, I should use 4 int''s (totalling 128-bits)? Or does this even matter anymore?

Russell
Neosmyle
Neosmyle
quote:
Original post by Cybertron
Using chess pieces as an example, you shouldn''t need to move any pieces. Each piece should have an X and Y position on the grid, rather than moving them around in a 2D array


Thats bad design. What about when someone wishes to promote a pawn to a queen when they already have one?

sark
sark
quote:
Original post by Neosmyle
Thats bad design. What about when someone wishes to promote a pawn to a queen when they already have one?




I always thought you could only promote a pawn when your queen was already captured. Of course, i could be wrong...

Write the WHOLE program first, profile, then optimise. Think of how much you could have got done if you hadnt kicked up such a fuss about the size of a struct, and then afterwards you realised that in the end it made a negliblable difference to the overall performance of your game anyway.

Besides, if you really are curious about such benign issues whats stopping you from writing a 15 minute program to test, time and compare several hundred thousand operations on unions/bitfields/plain structs?

Dont get me wrong, im not flaming you. Its just that you''ll get faster answers to your questions by attempting things for yourself. It is sometimes better to ask though, and you did learn something new by asking about this (anonymous structs).
Shannon Barber
Shannon Barber
quote:
Original post by Cybertron
Also packing bits or bytes uses more CPU power than simple memory acces


This is a bad assumption. If reading memory incurs a wait state while the data is brought in from system RAM to the CPU cache it can take alot longer than manipulating the bits.

In practice, you''re hard pressed to move more than 250MB/s in&out of system memory (and that''s at 100% CPU load). You can more data faster than that in short burst, or if you code it explicitly to move memory efficiently you can approach the theoretical limit. With normal code about 250MB/s is the speed limit (even with DDR & gigahertz machines).

quote:

And just to clarify one last time, I should not worry about using a small structure and just use 32-bit values for what I need? So instead of using 4 bytes (totalling 32-bits) for a piece, I should use 4 int''s (totalling 128-bits)? Or does this even matter anymore?


If there''s dozens to hundreds of the them, use the ints. If there''s going to be thousands-upon-thousands, you might try the bit fields. Of better yet, make a template concept for the implementation, and try both.
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Russell
Russell
quote:
Original post by Neosmyle
Thats bad design. What about when someone wishes to promote a pawn to a queen when they already have one?


That is perfectly legal. It is perfectly legal for one player to have 9 queens on the board at one time. It is not necessary for your queen to have been captured for you to promote to a queen.

In over the board play, this might be more complicated, since you don''t have 9 or 10 of every piece ''just in case'' some goof wants to waste his opponent''s time and promote 7 of his pawns to knights or something.

It is legal to do so however.

Russell

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.