When to use bit fields?

Should I use the implementation of the bit field C? If so, when has it ever been used?

I was looking at some emulator code, and it looks like registers for chips are not implemented using bit fields.

Is this something that can be avoided for performance reasons (or for some other reason)?

Is there still time when bit fields are used? (i.e. firmware to install actual chips, etc.)

+29
c ++ c bit-fields
Nov 21 '10 at 23:11
source share
11 answers

Bit fields are usually used only when it is necessary to map the structure fields to specific bits of bits, where some equipment will interpret the original bits. An example would be the collection of an IP packet header. I see no convincing reason for the emulator to model the register using bit fields, since it will never touch real equipment!

While bit fields can lead to neat syntaxes, they are quite platform dependent and therefore not portable. A more portable, but even more detailed approach is to use direct bitwise manipulation using offsets and bit masks.

If you use bit fields for anything other than assembling (or disassembling) structures on some physical interface, performance may suffer. This is because every time you read or write from a bit field, the compiler will have to generate code to mask and shift, which will write loops.

+14
Nov 21 '10 at 23:14
source share

One use of bit fields that have not been mentioned yet is that unsigned bit codes provide force-two arithmetic for free. For example, this:

 struct { unsigned x:10; } foo; 

arithmetic on foo.x will be executed modulo 2 10 = 1024.

(The same can be achieved directly using the bitwise operations & , of course, but sometimes this can lead to clearer code for the compiler to do this for you).

+12
Nov 22 2018-10-22T00:
source share

FWIW, and looking only at the relative performance question - a futuristic test:

 #include <time.h> #include <iostream> struct A { void a(unsigned n) { a_ = n; } void b(unsigned n) { b_ = n; } void c(unsigned n) { c_ = n; } void d(unsigned n) { d_ = n; } unsigned a() { return a_; } unsigned b() { return b_; } unsigned c() { return c_; } unsigned d() { return d_; } volatile unsigned a_:1, b_:5, c_:2, d_:8; }; struct B { void a(unsigned n) { a_ = n; } void b(unsigned n) { b_ = n; } void c(unsigned n) { c_ = n; } void d(unsigned n) { d_ = n; } unsigned a() { return a_; } unsigned b() { return b_; } unsigned c() { return c_; } unsigned d() { return d_; } volatile unsigned a_, b_, c_, d_; }; struct C { void a(unsigned n) { x_ &= ~0x01; x_ |= n; } void b(unsigned n) { x_ &= ~0x3E; x_ |= n << 1; } void c(unsigned n) { x_ &= ~0xC0; x_ |= n << 6; } void d(unsigned n) { x_ &= ~0xFF00; x_ |= n << 8; } unsigned a() const { return x_ & 0x01; } unsigned b() const { return (x_ & 0x3E) >> 1; } unsigned c() const { return (x_ & 0xC0) >> 6; } unsigned d() const { return (x_ & 0xFF00) >> 8; } volatile unsigned x_; }; struct Timer { Timer() { get(&start_tp); } double elapsed() const { struct timespec end_tp; get(&end_tp); return (end_tp.tv_sec - start_tp.tv_sec) + (1E-9 * end_tp.tv_nsec - 1E-9 * start_tp.tv_nsec); } private: static void get(struct timespec* p_tp) { if (clock_gettime(CLOCK_REALTIME, p_tp) != 0) { std::cerr << "clock_gettime() error\n"; exit(EXIT_FAILURE); } } struct timespec start_tp; }; template <typename T> unsigned f() { int n = 0; Timer timer; T t; for (int i = 0; i < 10000000; ++i) { ta(i & 0x01); tb(i & 0x1F); tc(i & 0x03); td(i & 0xFF); n += ta() + tb() + tc() + td(); } std::cout << timer.elapsed() << '\n'; return n; } int main() { std::cout << "bitfields: " << f<A>() << '\n'; std::cout << "separate ints: " << f<B>() << '\n'; std::cout << "explicit and/or/shift: " << f<C>() << '\n'; } 

Output on my test computer (numbers range from ~ 20% to run):

 bitfields: 0.140586 1449991808 separate ints: 0.039374 1449991808 explicit and/or/shift: 0.252723 1449991808 

Assumes that with g ++ -O3 on a fairly recent Athlon, bit fields are worse than several times slower than individual ints, and this particular implementation and / or / bithift is at least twice worse again ("worse" than other operations , such as reading / writing memory is emphasized by the volatility above, as well as the overhead in the cycle, etc., so the differences in underestimation of the results).

If you are dealing with hundreds of megabytes of structures, which can be mostly bitfields or basically different ints, caching problems can become dominant - so these are the benchmarks in your system.




UPDATE: user2188211 tried to edit, which was rejected, but it is useful to illustrate how bit fields become faster as the amount of data increases: "when repeating a vector of several million elements in the [modified version] of the above code, so the variables are not in the cache or registers, the bitfield code may be the fastest. "

 template <typename T> unsigned f() { int n = 0; Timer timer; std::vector<T> ts(1024 * 1024 * 16); for (size_t i = 0, idx = 0; i < 10000000; ++i) { T& t = ts[idx]; ta(i & 0x01); tb(i & 0x1F); tc(i & 0x03); td(i & 0xFF); n += ta() + tb() + tc() + td(); idx++; if (idx >= ts.size()) { idx = 0; } } std::cout << timer.elapsed() << '\n'; return n; } 

Results from run example (g ++ -03, Core2Duo):

  0.19016 bitfields: 1449991808 0.342756 separate ints: 1449991808 0.215243 explicit and/or/shift: 1449991808 



Of course, synchronization of all the relative ones and the way these fields are implemented may not matter at all in the context of your system.

+6
Nov 22 '10 at 3:21
source share

I saw / used bit fields in two situations: computer games and hardware interfaces. The hardware is pretty straight forward: the hardware expects data in a specific bit format, which you can define manually or through predefined library structures. It depends on the particular library, whether they use bit fields or just manipulate bits.

In the old days computers, bit fields were often used to maximize the use of computer / disk memory. For example, to define an NPC in an RPG, you can find (compiled example):

 struct charinfo_t { unsigned int Strength : 7; // 0-100 unsigned int Agility : 7; unsigned int Endurance: 7; unsigned int Speed : 7; unsigned int Charisma : 7; unsigned int HitPoints : 10; //0-1000 unsigned int MaxHitPoints : 10; //etc... }; 

You don’t see it so much in more modern games / software, as space savings deteriorate as computers get more memory. Saving 1 MB of memory when your computer has only 16 MB is a big deal, but not so much when you have 4 GB.

+5
Nov 21 '10 at 23:34
source share

Bit fields were used in the old days to save program memory.

They degrade performance because registers cannot work with them, so they must be converted to integers in order to do anything with them. They tend to lead to more complex code that is unable and harder to understand (since you have to mask and expose all the time in order to actually use the values.)

See the source at http://www.nethack.org/ to see pre ansi c in all its glory of the Beatles!

+2
Nov 21 '10 at 23:30
source share

One use for bit fields used to mirror hardware registers when writing inline code. However, since the order of the bits is platform dependent, they do not work if the equipment orders its own bits other than the processor. However, I can no longer think of using bits for fields. You are better off implementing a bit-manipulation library that can be ported across platforms.

+1
Nov 21 '10 at 23:19
source share

There is only one reason for using bitfields in modern code: to manage space requirements like bool or enum in a structure / class. For example (C ++):

 enum token_code { TK_a, TK_b, TK_c, ... /* less than 255 codes */ }; struct token { token_code code : 8; bool number_unsigned : 1; bool is_keyword : 1; /* etc */ }; 

IMO, in principle, does not make sense not to use bit-bit :1 for bool , since modern compilers will generate very efficient code for it. In C, however, make sure your bool typedef is either C99 _Bool , or that it doesn’t work, that unsigned int, because a signed one-bit field can only contain values 0 and -1 (unless you have a non-two-component machine).

With enumeration types, always use the size corresponding to the size of one of the primitive integer types (8/16/32/64 bits, on ordinary CPUs) to avoid inefficient code generation (repeated read-modify-write cycles, usually).

Using bit fields to build structures with some external data formats (packet headers, memory mapped I / O registers) is usually suggested, but I actually consider this a bad practice because C does not give you enough control over endianness, padding and ( for I / O registers) exactly what assembly sequences are going to. Take a look at the Ada submission suggestions someday if you want to see how much C is missing in this area.

+1
Nov 22 '10 at 0:27
source share

In the 70s, I used bit fields to control equipment on the trs80. The display / keyboard / cassette / discs were memory mapped devices. Individual bits controlled various things.

  • Column mapping 32 columns and 64 columns.
  • Bit 0 in the same memory location was input / output data in the cassette.

As far as I remember, there were several of them for controlling the drive. There were 4 bytes in total. I think a 2-bit disk has been selected. But it was a long time ago. It was pretty impressive at the time that there were at least two different c compilers for the plant shape.

Another observation is that bit fields are really platform specific. There is no expectation that a program with bit fields should migrate to another platform.

+1
May 29 '14 at 13:25
source share

Boost.Thread uses bitfields in its shared_mutex , at least on Windows:

  struct state_data { unsigned shared_count:11, shared_waiting:11, exclusive:1, upgrade:1, exclusive_waiting:7, exclusive_waiting_blocked:1; }; 
0
Nov 21 '10 at 23:38
source share

The main goal of bit fields is to provide a way to store memory in arrays with an aggregate set of data, creating a more dense data packing.

The whole idea is to take advantage of situations in which you have several fields in some type of structure that don't need the full width (and range) of some standard data type. This gives you the opportunity to pack several of these fields into one distribution unit, thereby reducing the overall size of the structure type. And an extreme example can be logical fields, which can be represented by individual bits (for example, 32 of them can be packed into a single block allocation unsigned int ).

Obviously, this only makes sense in a situation where the pluses of consuming reduced memory outweigh the minuses of slow access to values ​​stored in bit fields. However, such situations arise quite often, which makes bit-fields absolutely irreplaceable language features. This should answer your question about the modern use of bit fields: they are not only used, but are practically mandatory in any practically significant code oriented to processing large volumes of homogeneous data (for example, on large graphs, for example, by their example) - advantages Uses far outweigh any individual performance penalties.

In a sense, bit fields in their purpose are very similar to such "small" arithmetic types: signed/unsigned char , short , float . The actual data processing code usually does not use any types smaller than int or double (with a few exceptions). Arithmetic types of type signed/unsigned char , short , float exist only to serve as types of "storage": as compact elements with storage of structure types in situations where it is known that their range (or accuracy) is sufficient. Bit fields are another step in the same direction that brings better performance for the much greater benefits of memory savings.

So, this gives us a fairly clear-cut set of conditions under which it is worth using bit fields:

  • A structure type contains several fields that can be packed into fewer bits.
  • The program creates an instance of a large number of objects of this type of structure.

If the conditions are met, you declare all the bit packets of the fields adjacent (usually at the end of the structure type), assigning them the appropriate bit width (and usually take some steps to ensure that the bit width is appropriate). In most cases, it makes sense to play with ordering these fields to achieve the best packaging and / or performance.




There is also a strange secondary use of bit fields: using them to display groups of bits in various external representations, such as hardware registers, floating point formats, file formats, etc. It was never intended for the correct use of the bit field, although for some inexplicable reason this kind of abuse of the bit field continues to appear in real code. Just don't do it.

0
25 Oct '16 at 0:19
source share

An alternative to considering is to specify bitfield structures with a dummy structure (never created), where each byte represents a bit:

 struct Bf_format { char field1[5]; char field2[9]; char field3[18]; }; 

With this approach, sizeof gives the width of the bit field, and offsetof gives the offset of the bit field. At least in the case of GNU gcc, optimization of the compiler of bit operations (with constant shifts and masks) seems to have reached rough parity with bit fields (the base language).

I wrote a C ++ header file (using this approach) that allows you to define and use bitfield structures in performance, much more portable, much more flexible: https://github.com/wkaras/C-plus-plus-library- bit-fields . So, if you weren’t stuck using C, I think it would rarely be a good reason to use the core language facility for bit fields.

-2
Oct 25 '16 at 0:00
source share



All Articles