c++ - uint8 - uint32_t c
Uso de uint8, uint16, etc. (9)
¿Cuál es el uso de / benefit en el uso de un uint16 donde un uint32 también será suficiente (si hay alguno)?
Hay CPUs donde el unsigned char
es un valor de 16 bits. La prueba unitaria de dicho código sería difícil sin el uso de typedefs (uint16 es solo un typedef para el tipo apropiado).
Además, con el uso de estos typedefs, es más fácil construir en diferentes plataformas sin muchos problemas.
¿Habrá algún ahorro en el uso de memoria al usar tipos de datos más cortos (considerando la alineación de datos)?
No, eso no es un punto. Si uint16
es un typedef para unsigned short
, puede usar unsigned short
todas partes, pero puede obtener diferentes tipos en diferentes plataformas.
Por supuesto, el uso de un tipo más pequeño reducirá el consumo de memoria. Por ejemplo, usar uint16 en lugar de uint32, pero solo si usa matrices.
Si se trata de ahorrar unos pocos bytes de memoria, ¿es algo sensato hacer en hardware moderno?
Eso depende de la plataforma:
- menos uso de memoria significa menos errores de caché
- si se admite, existen funciones SIMD que procesan datos de 16 bits
Actualmente estoy trabajando con una base de código (C, C ++ mixta) para una plataforma MIPS de 32 bits. El procesador es bastante moderno [solo para mencionar que tenemos una buena cantidad de potencia de procesamiento y memoria].
La base de código usa tipos de datos como uint8 [entero ancho sin signo de 1 byte], uint16 [entero sin signo de ancho de 2 bytes], uint32 [entero sin signo de ancho de 4 bytes], etc.
Sé cómo el uso de estas construcciones es útil al portar el código a diferentes plataformas.
Mis preguntas son:
¿Cuál es el uso de / benefit en el uso de un uint16 donde un uint32 también será suficiente (si hay alguno)?
¿Habrá algún ahorro en el uso de memoria al usar tipos de datos más cortos (considerando la alineación de datos)?
Si se trata de ahorrar unos pocos bytes de memoria, ¿es algo sensato hacer en hardware moderno?
¿Cuál es el uso de / benefit en el uso de un uint16 donde un uint32 también será suficiente (si hay alguno)?
Si esos uint16s
son parte de arreglos o estructuras, puede guardar memoria y quizás ser capaz de manejar conjuntos de datos más grandes que con uint32s
en esas mismas matrices o estructuras. Realmente depende de tu código.
Los protocolos de datos y los formatos de archivo pueden usar uint16s
y, en su lugar, puede no ser correcto usar uint32s
. Esto depende del formato y la semántica (por ejemplo, si necesita valores para envolver de 65535 a 0, uint16
lo hará automáticamente, mientras que uint32
no lo hará).
OTOH, si esos uint16s
son solo variables locales o globales, reemplazarlas con las de 32 bits podría no significar una diferencia significativa porque es probable que ocupen el mismo espacio debido a la alineación y se pasen como parámetros de 32 bits (en la pila o en registros) en MIPS de todos modos.
¿Habrá algún ahorro en el uso de memoria al usar tipos de datos más cortos (considerando la alineación de datos)?
Puede haber ahorros, especialmente cuando los uint16s
son partes de muchas estructuras o elementos de grandes arreglos.
Si se trata de ahorrar unos pocos bytes de memoria, ¿es algo sensato hacer en hardware moderno?
Sí, se reduce el ancho de banda de la memoria (lo cual siempre es bueno) y se suelen reducir varios errores de caché (cachés de datos y TLB) cuando se opera con menos datos.
Ans. 1. El software tiene ciertos requisitos y especificaciones que le dicen estrictamente que tome solo 8/16 bits de un parámetro mientras codifica / descodifica o algún otro uso determinado. Por lo tanto, incluso si asigna un valor mayor a 127 en un u8, define los datos automáticamente para usted.
Ans. 2. No debemos olvidar que nuestros compiladores son mucho más que inteligentes para hacer la optimización, ya sea memoria o complejidad. Por lo tanto, siempre se recomienda utilizar una memoria más pequeña cuando sea posible.
Ans. 3. Por supuesto, guardar la memoria tiene sentido en el moderno h / w.
En primer lugar, si tiene tipos como uint16 definido, ¿dónde están definidos? No son tipos estándar, por lo que se definirán en algún encabezado de propiedad, tal vez el suyo o pueden ser suministrados por una biblioteca de terceros; en cuyo caso debe preguntarse qué tan portátil es ese código, y si está creando una dependencia que podría no tener sentido en alguna otra aplicación.
Otro problema es que muchas bibliotecas (de manera imprudente IMO) definen tales tipos con varios nombres como UINT16, uint16, U16 UI16 etc. que se convierte en algo así como una pesadilla asegurando el acuerdo de tipo y evitando conflictos de nombres. Si se definen tales nombres, lo ideal es que se coloquen en un espacio de nombres o se les proporcione un prefijo específico de la biblioteca para indicar con qué biblioteca se definieron para su uso, por ejemplo, rtos::uint16
a rtos_uint16
.
Dado que la biblioteca estándar ISO C99 proporciona tipos específicos de longitud de bits estándar en stdint.h, debe preferir su uso sobre cualquier definido en un encabezado propietario o de un tercero. Estos tipos tienen un sufijo _t
, por ejemplo, uint16_t
. En C ++, pueden colocarse en el std::
nombres std::
(aunque eso no es un hecho dado que el encabezado se introdujo en C99).
1] ¿Cuál es el uso de / benefit en el uso de un uint16 donde un uint32 también será suficiente (si hay alguno)?
Además de mi consejo anterior de preferir el uint16_t
stdint.h
, existen al menos dos razones legítimas para usar tipos específicos de longitud:
- Para coincidir con un ancho de registro de hardware específico.
- Para hacer cumplir una API común y compatible en diferentes arquitecturas.
2] ¿Habrá algún ahorro en el uso de memoria al usar tipos de datos más cortos (considerando la alineación de datos)?
Posiblemente, pero si la memoria no es su problema, esa no es una buena razón para usarlos. Vale la pena considerarlo quizás para objetos de datos grandes o matrices, pero aplicarlo globalmente rara vez vale la pena el esfuerzo.
3] Si es para salvar unos pocos bytes de memoria, ¿es algo sensato hacer en hardware moderno?
Ver [2]. " Hardware moderno ", sin embargo, no implica necesariamente grandes recursos; hay muchos dispositivos ARM Cortex-M de 32 bits con solo unos pocos Kb de RAM, por ejemplo. Eso es más sobre el espacio, el costo y el consumo de energía que sobre la edad de diseño o arquitectura.
Las respuestas a sus preguntas se reducen a un concepto clave: ¿Qué tan grande es la información? Si está procesando mucho, es obvio el beneficio de utilizar tipos de datos más pequeños. Piénselo de esta manera: simplemente calculando la cepa conocida más grande recientemente descubierta podría acabar sin memoria en una estación de trabajo típica. El número en sí mismo lleva más de un gigabyte solo para almacenar. Eso no incluye trabajar hasta calcular el número real. Si tuviera que usar un tipo de datos gruesos en lugar de uno delgado, en su lugar podría estar buscando dos gigabytes. Un ejemplo simplista, pero bueno, no obstante.
Se debe verificar el código / ensamblador de la máquina producida para verificar que haya algún código guardado. En las arquitecturas de tipo RISC, el inmediato típico es de 16 bits, pero el uso de uint16_t consumirá un registro completo de 32 bits; por lo tanto, si usa tipos int, pero se compromete a usar valores cercanos a cero producirá los mismos resultados y será más portátil.
La memoria de ahorro IMO también vale la pena en las plataformas modernas. Un código más estricto conduce a, por ejemplo, una mejor duración de la batería y un UX más fluido. Sin embargo, sugiero microgestión del tamaño solo cuando se trabaja con arreglos (grandes), o cuando la variable se asigna a algún recurso real de HW.
PD. Los compiladores son inteligentes, pero las personas que los escriben funcionan en este momento y los hacen aún mejores.
Usar uint16_t
lugar de uint32_t
es guardar memoria. También podría ser una restricción de hardware (por ejemplo, algún controlador periférico realmente está enviando 16 bits). Sin embargo, puede que no valga la pena usarlo, debido a consideraciones de caché y alineamiento (realmente debe comparar).
Usar tipos enteros de ancho exacto como int32_t
y friends es útil para evitar errores de extensión de signo entre plataformas que tienen tamaños diferentes para int
y long
. Esto puede ocurrir al aplicar máscaras de bits o al cambiar de bit, por ejemplo. Si realiza estas operaciones en un long
por ejemplo, y su código funciona para una long
32 bits, podría romperse por una long
64 bits. Si, por otro lado, usa uint32_t
, sabrá exactamente qué resultados obtendrá independientemente de la plataforma.
También son útiles para intercambiar datos binarios entre plataformas, donde solo tiene que preocuparse por la endianidad de los datos almacenados y no por el ancho del bit; si escribe un int64_t
en un archivo, sabrá que en otra plataforma puede leerlo y almacenarlo en un int64_t
. Si estuvieras escribiendo un texto long
vez de 64 bits en una plataforma, podrías terminar necesitando long long
en otra plataforma porque solo hay 32 bits.
Ahorrar memoria generalmente no es la razón, a menos que esté hablando de entornos muy limitados (elementos incrustados) o grandes conjuntos de datos (como una matriz con 50 millones de elementos o similares).
cstdint
tiene un montón de cstdint
para diferentes propósitos.
-
intN_t
para un ancho específico -
int_fastN_t
para el entero más rápido, que tiene al menos N bits -
int_leastN_t
para el entero más pequeño, que tiene al menos N bits - Sus equivalentes
unsigned
Debe elegir según sus circunstancias. ¿Almacena miles en un std::vector
y no hace muchos cálculos? intN_t
es probablemente tu hombre. ¿Necesita un cálculo rápido en una pequeña cantidad de números enteros? int_fastN_t
es probablemente tu hombre.