Trabajo casi exclusivamente en C ++ 11/14, y generalmente me estremezco cuando veo un código como este:
std::int64_t mArray;
mArray |= someMask << 1;
Esto es solo un ejemplo; Estoy hablando de manipulación de bits en general. En C ++, ¿hay realmente algún punto? Lo anterior es alucinante y propenso a errores, mientras que el uso de le std::bitset
permite:
- modifique más fácilmente el tamaño de la
std::bitset
que sea necesario ajustando un parámetro de plantilla y permitiendo que la implementación se encargue del resto, y - Dedique menos tiempo a descubrir qué está sucediendo (y posiblemente cometer errores) y escriba
std::bitset
de manera similar astd::array
otros contenedores de datos.
Mi pregunta es; ¿Hay alguna razón para no usar std::bitset
sobre tipos primitivos, aparte de la compatibilidad con versiones anteriores?
c++
c++11
bitwise-operators
cuant
fuente
fuente
std::bitset
se fija en tiempo de compilación. Ese es el único inconveniente paralizante que se me ocurre.std::bitset
manipulación de bits frente al estilo c (por ejemploint
), que también se corrige en tiempo de compilación.std::bitset
no estaba disponible (o el autor no lo conocía) y no ha habido una razón para reescribir el código para usarlostd::bitset
.bitset
uno es, pero un pequeño vector o conjunto deint
s (de índice de bits) también podría ser legítimo. La filosofía de C / C ++ no oculta estas complejidades de elección del programador.Respuestas:
Desde un punto de vista lógico (no técnico), no hay ventaja.
Cualquier código C / C ++ simple se puede incluir dentro de una "construcción de biblioteca" adecuada. Después de tal ajuste, la cuestión de "si esto es más ventajoso que eso" se convierte en una cuestión discutible.
Desde el punto de vista de la velocidad, C / C ++ debería permitir que la construcción de la biblioteca genere código que sea tan eficiente como el código simple que envuelve. Sin embargo, esto está sujeto a:
Usando este tipo de argumento no técnico, cualquier "función faltante" podría ser agregada por cualquier persona y, por lo tanto, no se considera una desventaja.
Sin embargo, los requisitos y limitaciones incorporados no se pueden superar con código adicional. A continuación, sostengo que el tamaño de
std::bitset
es una constante de tiempo de compilación y, por lo tanto, si bien no se considera una desventaja, sigue siendo algo que afecta la elección del usuario.Desde un punto de vista estético (legibilidad, facilidad de mantenimiento, etc.), hay una diferencia.
Sin embargo, no es aparente que el
std::bitset
código inmediatamente gane sobre el código C simple. Uno tiene que mirar piezas de código más grandes (y no una muestra de juguete) para decir si el uso destd::bitset
ha mejorado la calidad humana del código fuente.La velocidad de la manipulación de bits depende del estilo de codificación. El estilo de codificación afecta tanto a la manipulación de bits C / C ++, y también es igualmente aplicable
std::bitset
, como se explica a continuación.Si uno escribe código que usa el
operator []
para leer y escribir un bit a la vez, tendrá que hacerlo varias veces si hay más de un bit para manipular. Lo mismo puede decirse del código de estilo C.Sin embargo,
bitset
también tiene otros operadores, comooperator &=
,operator <<=
etc., que opera en todo el ancho del conjunto de bits. Debido a que la maquinaria subyacente a menudo puede operar en 32 bits, 64 bits y, a veces, 128 bits (con SIMD) a la vez (en el mismo número de ciclos de CPU), código diseñado para aprovechar tales operaciones de múltiples bits puede ser más rápido que el código de manipulación de bits "en bucle".La idea general se llama SWAR (SIMD dentro de un registro) , y es un subtema bajo manipulaciones de bits.
Algunos proveedores de C ++ pueden implementar
bitset
entre 64 bits y 128 bits con SIMD. Es posible que algunos proveedores no lo hagan (pero eventualmente lo harán). Si es necesario saber qué está haciendo la biblioteca del proveedor de C ++, la única forma es mirar el desmontaje.En cuanto a si
std::bitset
tiene limitaciones, puedo dar dos ejemplos.std::bitset
debe ser conocido en tiempo de compilación. Para hacer una matriz de bits con un tamaño elegido dinámicamente, uno tendrá que usarstd::vector<bool>
.std::bitset
no proporciona una forma de extraer una porción consecutiva de N bits de unabitset
M mayor .El primero es fundamental, lo que significa que para las personas que necesitan conjuntos de bits de tamaño dinámico, deben elegir otras opciones.
El segundo se puede superar, porque uno puede escribir algún tipo de adaptadores para realizar la tarea, incluso si el estándar
bitset
no es extensible.Existen ciertos tipos de operaciones SWAR avanzadas que no se proporcionan desde el primer momento
std::bitset
. Uno podría leer sobre estas operaciones en este sitio web sobre permutaciones de bits . Como de costumbre, uno puede implementarlos por su cuenta, operando ademásstd::bitset
.En cuanto a la discusión sobre el rendimiento.
Una advertencia: mucha gente pregunta por qué (algo) de la biblioteca estándar es mucho más lento que un código simple de estilo C. No repetiría los requisitos previos de microbenchmarking aquí, pero solo tengo este consejo: asegúrese de comparar en el "modo de lanzamiento" (con optimizaciones habilitadas), y asegúrese de que el código no se elimine (eliminación de código muerto) o sea izada fuera de un bucle (movimiento de código invariante de bucle) .
Dado que, en general, no podemos decir si alguien (en Internet) estaba haciendo los microbenchmarks correctamente, la única forma en que podemos llegar a una conclusión confiable es hacer nuestros propios microbenchmarks, documentar los detalles y someterlos a revisión y crítica pública. No está de más rehacer microbenchmarks que otros han hecho antes.
fuente
std::bitset
. No hay garantía de coherencia de memoria (instd::bitset
), lo que significa que no se debe compartir entre núcleos. Las personas que necesitan compartirlo entre núcleos tenderán a construir su propia implementación. Cuando los datos se comparten entre diferentes núcleos, es habitual alinearlos con el límite de la línea de caché. No hacerlo reduce el rendimiento y expone más dificultades de no atomicidad. No tengo suficiente conocimiento para dar una visión general sobre cómo construir una implementación paralelizable destd::bitset
.bitset
voluntad.Ciertamente, esto no se aplica en todos los casos, pero ocasionalmente un algoritmo puede depender de la eficiencia del giro de bits de estilo C para proporcionar ganancias de rendimiento significativas. El primer ejemplo que me viene a la mente es el uso de bitboards , codificaciones enteras inteligentes de las posiciones de los juegos de mesa, para acelerar los motores de ajedrez y similares. Aquí, el tamaño fijo de los tipos enteros no es un problema, ya que los tableros de ajedrez siempre son 8 * 8 de todos modos.
Para un ejemplo simple, considere la siguiente función (tomada de esta respuesta por Ben Jackson ) que prueba una posición de Connect Four para la victoria:
fuente
std::bitset
sería más lento?