Fwiw, y mirar sólo a la cuestión rendimiento relativo - un punto de referencia bodgy:
#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)
{
t.a(i & 0x01);
t.b(i & 0x1F);
t.c(i & 0x03);
t.d(i & 0xFF);
n += t.a() + t.b() + t.c() + t.d();
}
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';
}
salida en mi máquina de prueba (los números varían según el ~ 20% de ejecución en ejecución):
bitfields: 0.140586
1449991808
separate ints: 0.039374
1449991808
explicit and/or/shift: 0.252723
1449991808
Sugiere que con g ++ -O3 en un Athlon bastante reciente, los bitfields son peores que unas pocas veces más lentos que los ints separados, y esta implementación particular y/o/bitshift es al menos dos veces peor ("peor" que otras operaciones como memory read/las escrituras se enfatizan por la volatilidad anterior, y hay un ciclo sobrecarga, etc., por lo que las diferencias son subestimadas en los resultados).
Si se trata de cientos de megabytes de estructuras que pueden ser principalmente bitfields o ints principalmente distintos, los problemas de caché pueden volverse dominantes, por lo que el benchmark en su sistema.
ACTUALIZACIÓN: user2188211 intentó una edición que fue rechazada, pero útilmente ilustra la forma en campos de bits son más rápidos como la cantidad de datos aumenta: "cuando la iteración en un vector de unos pocos millones de elementos en [una versión modificada de] los anteriores código, de modo que las variables no residen en caché o registros, el código del campo de bits puede ser el más rápido ".
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];
t.a(i & 0x01);
t.b(i & 0x1F);
t.c(i & 0x03);
t.d(i & 0xFF);
n += t.a() + t.b() + t.c() + t.d();
idx++;
if (idx >= ts.size()) {
idx = 0;
}
}
std::cout << timer.elapsed() << '\n';
return n;
}
Resultados sobre de un ejemplo de ejecución (g ++ -03, Core2Duo):
0.19016
bitfields: 1449991808
0.342756
separate ints: 1449991808
0.215243
explicit and/or/shift: 1449991808
Por supuesto, de momento toda forma relativa, y que se implementa estos campos puede no importar en absoluto en el contexto de tu sistema
en relación con el problema de "ciclos de grabación", he descubierto que de hecho era más rápido usar el tipo entero más pequeño posible en lugar de utilizar campos de bits. A excepción de las banderas booleanas (para las cuales el enmascaramiento es fácil y no se requieren cambios), por lo tanto estoy de acuerdo contigo :) –
@Matthieu: Me imagino que en * la mayoría de las circunstancias, usar 'int' sería más rápido, porque es la plataforma ancho nativo La excepción a esto sería si hacer todo como 'int' hace que sus estructuras de datos sean significativamente más grandes, causando fallas en la memoria caché, etc. –
@OliCharlesworth, el problema de la red little-endian o big-endian hará que use el encabezado del paquete de paso de bit-campo ha fallado. Y el estándar de C++ tampoco define cómo se almacena el campo de bits, sino que es específico de la implementación. Y la base en el rendimiento del campo de bits no es buena, el campo de bits es inútil. – ZijingWu