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::bitsetpermite:
- modifique más fácilmente el tamaño de la
std::bitsetque 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::bitsetde manera similar astd::arrayotros contenedores de datos.
Mi pregunta es; ¿Hay alguna razón para no usar std::bitsetsobre tipos primitivos, aparte de la compatibilidad con versiones anteriores?
c++
c++11
bitwise-operators
cuant
fuente
fuente

std::bitsetse fija en tiempo de compilación. Ese es el único inconveniente paralizante que se me ocurre.std::bitsetmanipulación de bits frente al estilo c (por ejemploint), que también se corrige en tiempo de compilación.std::bitsetno estaba disponible (o el autor no lo conocía) y no ha habido una razón para reescribir el código para usarlostd::bitset.bitsetuno es, pero un pequeño vector o conjunto deints (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::bitsetes 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::bitsetcó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::bitsetha 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,
bitsettambié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
bitsetentre 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::bitsettiene limitaciones, puedo dar dos ejemplos.std::bitsetdebe 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::bitsetno proporciona una forma de extraer una porción consecutiva de N bits de unabitsetM 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
bitsetno 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.bitsetvoluntad.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::bitsetsería más lento?