¿Por qué los flotadores siguen siendo parte del lenguaje Java cuando se recomiendan los dobles en su lugar?

84

En todos los lugares que he visto, dice que doublees superior floaten casi todos los sentidos. floatse ha vuelto obsoleto doubleen Java, entonces ¿por qué todavía se usa?

floatProgramo mucho con Libgdx, y te obligan a usar (deltaTime, etc.), pero me parece que doublees más fácil trabajar en términos de almacenamiento y memoria.

También leí ¿ Cuándo usas flotante y cuándo usas doble , pero si floatrealmente solo es bueno para números con muchos dígitos después del punto decimal, entonces por qué no podemos usar una de las muchas variaciones de double?

¿Hay alguna razón por la cual la gente insiste en usar flotadores a pesar de que ya no tiene ninguna ventaja? ¿Es demasiado trabajo cambiarlo todo?

Eames
fuente
8
Posible duplicado de Cuándo usas flotador y cuándo usas doble
Vincent Savard
58
¿Cómo demonios inferiste "flotar es realmente bueno solo para números con muchos dígitos después del punto decimal" de las respuestas a esa pregunta? ¡Dicen todo lo contrario !
Ordous
20
@Eames Observe cómo dice "números", no "dígitos". Los flotadores son peores cuando necesita precisión o rango, son mejores cuando necesita montones y montones de datos no tan precisos. Eso es lo que dicen esas respuestas.
Ordous
29
¿Por qué tenemos bytey shorty intcuando no long?
user253751
15
Una pregunta mucho más adecuada es "¿por qué eliminaría una palabra clave y un tipo de datos primitivo de un lenguaje con décadas de código que simplemente se rompería sin razón alguna?"
sara

Respuestas:

169

LibGDX es un marco utilizado principalmente para el desarrollo de juegos.

En el desarrollo de juegos, generalmente tienes que hacer muchos cálculos numéricos en tiempo real y cualquier rendimiento que puedas obtener es importante. Es por eso que los desarrolladores de juegos suelen usar flotante siempre que la precisión de flotación sea lo suficientemente buena.

El tamaño de los registros de FPU en la CPU no es lo único que debe tener en cuenta en este caso. De hecho, la mayor parte de la gran acumulación de números en el desarrollo del juego la realiza la GPU, y las GPU generalmente están optimizadas para flotadores, no para dobles .

Y luego también está:

  • ancho de banda del bus de memoria (qué tan rápido puede traspasar datos entre RAM, CPU y GPU)
  • Caché de la CPU (que hace que lo anterior sea menos necesario)
  • RAM
  • VRAM

que son recursos preciosos de los cuales obtienes el doble cuando usas flotante de 32 bits en lugar de doble de 64 bits.

Philipp
fuente
2
¡Gracias! Esto realmente ayudó porque profundizó en lo que cambió el uso de memoria y por qué
Eames
77
Además, para las operaciones SIMD, los valores de 32 bits pueden tener el doble de rendimiento. Como señala la respuesta de 8bittree , las GPU tienen una penalización de rendimiento aún mayor con doble precisión.
Paul A. Clayton
55
Muchas tuberías gráficas incluso admiten medios flotantes de 16 bits para aumentar el rendimiento donde la precisión es suficiente.
Adi Shavit
22
@phresnel Todos lo son. Tienes que mover posiciones, actualizar datos y lo que no. Y esta es la parte simple . Luego debes renderizar (= leer, rotar, escalar y traducir) las texturas, distancias, llevarlo al formato de pantalla ... Hay mucho por hacer.
Sebb
8
@phresnel como ex vicepresidente de operaciones de una empresa de desarrollo de juegos, te aseguro que en casi todos los juegos hay muchos cálculos numéricos. Tenga en cuenta que generalmente está contenido en bibliotecas y 100% abstraído del ingeniero, espero que entiendan y respeten que todo ese proceso está ocurriendo. ¿Raíz cuadrada inversa mágica, alguien?
corsiKa
57

Los flotadores usan la mitad de la memoria que los dobles.

Pueden tener menos precisión que los dobles, pero muchas aplicaciones no requieren precisión. Tienen un rango mayor que cualquier formato de punto fijo de tamaño similar. Por lo tanto, llenan un nicho que necesita amplios rangos de números pero que no necesita alta precisión, y donde el uso de memoria es importante. Los he usado para grandes sistemas de redes neuronales en el pasado, por ejemplo.

Al moverse fuera de Java, también se usan ampliamente en gráficos 3D, porque muchas GPU las usan como su formato principal; fuera de los dispositivos NVIDIA Tesla / AMD FirePro muy caros, el punto flotante de doble precisión es muy lento en las GPU.

Jules
fuente
8
Hablando de redes neuronales, CUDA actualmente tiene soporte para variables de punto flotante de media precisión (16 bits), incluso menos precisas pero con huellas de memoria aún más bajas, debido al mayor uso de aceleradores para el trabajo de aprendizaje automático.
JAB
Y cuando programa FPGAs, tiende a seleccionar la cantidad de bits para mantisa y exponente manualmente cada vez: v
Sebi
48

Compatibilidad al revés

Esta es la principal razón para mantener el comportamiento en una ya existente lenguaje / biblioteca / ISA / etc.

Considere lo que sucedería si sacaran flotadores de Java. Libgdx (y miles de otras bibliotecas y programas) no funcionarían. Se necesitará mucho esfuerzo para actualizar todo, posiblemente años para muchos proyectos (solo mire la transición de Python 2 a Python 3 que rompe la compatibilidad con versiones anteriores). Y no todo se actualizará, algunas cosas se romperán para siempre porque los encargados del mantenimiento las abandonaron, tal vez antes de lo que lo harían porque requeriría más esfuerzo del que desean actualizar, o porque ya no es posible lograr lo que se suponía que su software suponía. que hacer.

Actuación

Los dobles de 64 bits ocupan el doble de memoria y casi siempre son más lentos de procesar que los flotantes de 32 bits (las excepciones muy raras son donde se espera que la capacidad de flotación de 32 bits se use tan raramente o nada, que no se hizo ningún esfuerzo para optimizarlos) A menos que esté desarrollando hardware especializado, no lo experimentará en el futuro cercano).

Especialmente relevante para ti, Libgdx es una biblioteca de juegos. Los juegos tienden a ser más sensibles al rendimiento que la mayoría del software. Y las tarjetas gráficas para juegos (es decir, AMD Radeon y NVIDIA Geforce, no FirePro o Quadro) tienden a tener un rendimiento de coma flotante de 64 bits muy débil. Cortesía de Anandtech, así es como el rendimiento de precisión doble se compara con el rendimiento de precisión simple en algunas de las principales tarjetas de juego de AMD y NVIDIA disponibles (a principios de 2016)

AMD
Card    R9 Fury X      R9 Fury       R9 290X    R9 290
FP64    1/16           1/16          1/8        1/8

NVIDIA
Card    GTX Titan X    GTX 980 Ti    GTX 980    GTX 780 Ti
FP64    1/32           1/32          1/32       1/24

Tenga en cuenta que las series R9 Fury y GTX 900 son más nuevas que las series R9 200 y GTX 700, por lo que el rendimiento relativo para el punto flotante de 64 bits está disminuyendo. Regrese lo suficientemente lejos y encontrará el GTX 580, que tenía una relación de 1/8 como la serie R9 200.

1/32 del rendimiento es una penalización bastante grande a pagar si tienes una restricción de tiempo apretada y no ganas mucho usando el doble más grande.

8bittree
fuente
1
tenga en cuenta que el rendimiento para el punto flotante de 64 bits está disminuyendo en relación con el rendimiento de 32 bits debido a las instrucciones de 32 bits cada vez más altamente optimizadas, no porque el rendimiento real de 64 bits esté disminuyendo. también depende del punto de referencia real utilizado; Me pregunto si el déficit de rendimiento de 32 bits resaltado en estos puntos de referencia se debe a problemas de ancho de banda de memoria, así como a la velocidad computacional real
sig_seg_v
Si va a hablar sobre el rendimiento DP en las tarjetas gráficas, definitivamente debe mencionar el Titan / Titan Black. Ambos cuentan con modificaciones que permiten que la tarjeta alcance 1/3 de rendimiento, a costa de un rendimiento de precisión único.
SGR
@sig_seg_v Definitivamente, hay al menos algunos casos en los que el rendimiento de 64 bits disminuye absolutamente, no solo relativamente. Vea estos resultados para obtener un punto de referencia Folding @ Home de doble precisión, donde una GTX 780 Ti supera a una GTX 1080 (otra tarjeta de relación 1/32) y una 980 Ti, y en el lado de AMD, la 7970 (una tarjeta de relación 1/4) , así como el R9 290 y el R9 290X vencieron a la serie R9 Fury. Compare eso con la versión de precisión única del punto de referencia , donde las tarjetas más nuevas superan fácilmente a sus predecesoras.
8bittree
36

Operaciones atómicas

Además de lo que otros ya han dicho, una desventaja específica de Java de double(y long) es que no se garantiza que las asignaciones a tipos primitivos de 64 bits sean atómicas . De la Especificación del lenguaje Java, Java SE 8 Edition , página 660 (énfasis agregado):

17.7 Tratamiento no atómico de doubleylong

Para los propósitos del modelo de memoria del lenguaje de programación Java, una única escritura en un valor longo no volátil doublese trata como dos escrituras separadas: una para cada mitad de 32 bits. Esto puede dar como resultado una situación en la que un subproceso ve los primeros 32 bits de un valor de 64 bits de una escritura y los segundos 32 bits de otra escritura.

Yuck

Para evitar esto, debe declarar la variable de 64 bits con lavolatile palabra clave, o utilizar alguna otra forma de sincronización en torno a las asignaciones.

Kevin J. Chase
fuente
2
¿No necesita sincronizar el acceso simultáneo a entradas y flotantes de todos modos para evitar actualizaciones perdidas y hacer que sean volátiles para evitar el almacenamiento en caché excesivo? ¿Me equivoco al pensar que lo único que impide la atomicidad int / float es que nunca pueden contener valores "mixtos" que no debían tener?
Traubenfuchs
3
@Traubenfuchs Eso es, de hecho, lo que está garantizado allí. El término que he oído usar para él es "desgarro", y creo que captura el efecto bastante bien. El modelo de lenguaje de programación Java garantiza que los valores de 32 bits, cuando se lean, tendrán un valor que se les escribió en algún momento. Esa es una garantía sorprendentemente valiosa.
Cort Ammon
Este punto sobre la atomicidad es súper importante. Wow, me había olvidado de este importante hecho. Contraintuitivo, ya que podemos tender a pensar que los primitivos son atómicos por naturaleza. Pero no atómico en este caso.
Basil Bourque
3

Parece que otras respuestas omitieron un punto importante: las arquitecturas SIMD pueden procesar menos / más datos dependiendo de si operan doubleo floatestructuras (por ejemplo, ocho valores flotantes a la vez o cuatro valores dobles a la vez).

Resumen de consideraciones de rendimiento

  • float puede ser más rápido en ciertas CPU (por ejemplo, ciertos dispositivos móviles).
  • float usa menos memoria, por lo que en grandes conjuntos de datos puede reducir sustancialmente la memoria total requerida (disco duro / RAM) y el ancho de banda consumido.
  • float puede hacer que una CPU consuma menos energía (no puedo encontrar una referencia, pero si no es posible al menos parece plausible) para los cálculos de precisión simple en comparación con los cálculos de precisión doble.
  • float consume menos ancho de banda, y en algunas aplicaciones eso importa.
  • Las arquitecturas SIMD pueden procesar hasta el doble de la misma cantidad de datos porque generalmente.
  • float usa tanto como la mitad de la memoria caché en comparación con el doble.

Resumen de consideraciones de precisión

  • En muchas aplicaciones floates suficiente
  • double tiene mucha más precisión de todos modos

Consideraciones de compatibilidad

  • Si sus datos deben enviarse a una GPU (por ejemplo, para un videojuego que utiliza OpenGL o cualquier otra API de representación), el formato de punto flotante es considerablemente más rápido que double(eso es porque los fabricantes de GPU intentan aumentar el número de núcleos gráficos, y Por lo tanto, intentan guardar la mayor cantidad de circuitos posible en cada núcleo, por lo que la optimización floatpermite crear GPU con más núcleos en el interior)
  • Las GPU antiguas y algunos dispositivos móviles simplemente no pueden aceptar doublecomo formato interno (para operaciones de renderizado 3D)

Consejos generales

  • En los procesadores de escritorio modernos (y probablemente en una buena cantidad de procesadores móviles), básicamente puede suponer que el uso de doublevariables temporales en la pila proporciona precisión adicional de forma gratuita (precisión adicional sin penalización de rendimiento).
  • Nunca use más precisión de la que necesita (es posible que no sepa cuánta precisión realmente necesita).
  • A veces, el rango de valores lo obliga (algunos valores serían infinitos si está usando float, pero pueden ser valores limitados si está usando double)
  • Usar solo floato solo doubleayuda enormemente al compilador a SIMD-ify las instrucciones.

Vea los comentarios a continuación de PeterCordes para obtener más información.

Desarrollador de juegos
fuente
1
doublelos temporales solo son gratuitos en x86 con la x87 FPU, no con SSE2. Auto-vectorización de un bucle con doublelos temporales significa desembalaje floata double, que tiene una instrucción adicional, y procesar la mitad de los elementos por vectores. Sin la vectorización automática, la conversión generalmente puede ocurrir sobre la marcha durante una carga o almacenamiento, pero significa instrucciones adicionales cuando se mezclan flotantes y dobles en expresiones.
Peter Cordes
1
En las CPU x86 modernas, div y sqrt son más rápidos para flotar que el doble, pero otras cosas son la misma velocidad (sin contar el problema de ancho de vector SIMD o el ancho de banda de memoria / huella de caché, por supuesto).
Peter Cordes
@PeterCordes gracias por expandir algunos puntos. No era consciente de la disparidad div y sqrt
GameDeveloper
0

Aparte de las otras razones que se mencionaron:

Si tiene datos de medición, ya sean presiones, flujos, corrientes, voltajes o lo que sea, esto a menudo se hace con hardware que tenga un ADC.

Un ADC generalmente tiene 10 o 12 bits, los de 14 o 16 bits son más raros. Pero sigamos con el de 16 bits: si mide alrededor de la escala completa, tiene una precisión de 1/65535. Eso significa que un cambio de 65534/65535 a 65535/65535 es solo este paso: 1/65535. Eso es aproximadamente 1.5E-05. La precisión de un flotador es de alrededor de 1E-07, por lo que es mucho mejor. Eso significa que no pierde nada al usar floatpara almacenar estos datos.

Si hace cálculos excesivos con flotadores, tiene un rendimiento ligeramente peor que doublesen términos de precisión, pero a menudo no necesita esa precisión, ya que a menudo no le importa si solo midió un voltaje de 2 V o 2.00002 V. De manera similar , si convierte este voltaje en presión, no le importa si tiene 3 bar o 3.00003 bar.

glglgl
fuente