Design: Documentar por qué los bits de NaN no son completamente deterministas

Creado en 22 mar. 2016  ·  15Comentarios  ·  Fuente: WebAssembly/design

(Actualmente no estoy abogando; esto es para que tomemos una decisión informada y recopilemos material para una justificación).

En cuanto a Nondeterminism.md , se destacan las partes de bits de NaN. Por supuesto, los subprocesos pueden correr, los recursos se pueden agotar y se pueden agregar funciones; esas son consecuencias del diseño general. ¿Pero bits de NaN? ¿Podrían las máquinas virtuales ser un poco más inteligentes y encargarse de esto? Hay dos cuestiones:

Cuando una operación obtiene más de un operando NaN, ¿cuál se propaga?

IEEE 754 deja esto sin especificar, pero al menos x86, ARM y Power eligen el "primer" operando, y es una opción razonable.

Sin embargo, arreglar la elección en el nivel de wasm significaría que las implementaciones de wasm no pueden conmutar sumas y multiplicaciones de punto flotante, lo que a veces es una optimización útil en pre-VEX x86 donde las instrucciones golpean una de sus entradas. Además, requeriría que cualquiera que use una máquina virtual basada en LLVM le enseñe que sumar y multiplicar no son conmutativos en wasm.

Se podría argumentar razonablemente que estos no son obstáculos, por lo que este problema se puede solucionar teóricamente si hay un fuerte deseo.

Cuando una operación produce un NaN y no tiene operandos NaN, ¿cuál es el bit de signo?

x86 usa 1, ARM usa 0.

La forma más sencilla de solucionar este problema sería canonicalizar después de cada operación de punto flotante. Esto es factible, aunque un truco es que tanto el 0 como el 1 son posibles valores válidos cuando hay un operando NaN para propagar, por lo que no sería suficiente con solo buscar un resultado NaN y canonicalizar; uno tendría que verificar un resultado de NaN y la falta de operandos de NaN, y solo entonces canonicalizar.

Canonicalizar después de cada operación sería muy costoso; otra opción es simplemente canonicalizar en los puntos de "escape" de los cálculos (la ruta de canonicalización tendría que reproducir esencialmente un flujo de cálculo completo para determinar la salida de NaN correcta). Esta es una mejora, pero es probable que aún agregue una cantidad significativa de gastos generales.

Otra posible implementación sería desenmascarar la excepción no válida, tomar una trampa cada vez que se genera un NaN y luego realizar la canonicalización y regresar. Las desventajas incluyen el uso de un modo de CPU no predeterminado y ser muy lento si se generan muchos inválidos.

Desafortunadamente, estos enfoques tienen importantes inconvenientes. A menos que surjan otras ideas o haya un deseo muy fuerte, esto parece difícil de arreglar.


Otra cosa a tener en cuenta es que los bits NaN son difíciles de observar accidentalmente, por lo que esta no es una preocupación importante de portabilidad en general.

¿Alguien más tiene alguna idea que agregar?

clarification floating point

Comentario más útil

Me gustaría cuantificar los efectos de esto en el rendimiento antes de tomar una decisión. Creo que nuestras cadenas de herramientas todavía son demasiado inmaduras para realizar buenas mediciones de rendimiento en este momento (hay polos más largos en el camino de esta). Dicho de otra manera: quiero evitar "la muerte por mil cortes".

Todos 15 comentarios

Otra cosa a tener en cuenta es que los bits NaN son difíciles de observar accidentalmente

¿Hay alguna forma de hacerlos imposibles de observar o al menos el bit de señal?

En mi opinión, los no determinismos de NaN deben dejarse en paz, dado que es muy raro que el software se preocupe por ellos. ¿Cuál es el caso de sacrificar el rendimiento para hacerlos deterministas?

Aquí hay una lista de las formas en que se pueden observar los bits de NaN:

  • reinterpret conversión
  • store a la memoria lineal y cargue los bits con una interpretación diferente (o deje que los bits se observen externamente)
  • pasar un argumento a call de una función importada, o devolver un valor de una función exportada
  • copysign el signo mordió en un no-NaN

Las máquinas virtuales podrían insertar código canonicalizador antes de cada uno de estos; esa es la idea de "canonicalizar en puntos de escape" discutida anteriormente. store es muy común en rutas calientes, por lo que probablemente aún sea bastante costoso.

@qwertie Me aquí, hay formas de hacerlo completamente determisítico, incluso si no entra en la especificación.

@qwertie Los beneficios pueden incluir una portabilidad ligeramente mayor (existe código en el mundo que utiliza imprudentemente la función IEEE 754 totalOrder, por ejemplo), una reproducibilidad ligeramente mayor e invariantes más fuertes al realizar cálculos simétricos en varios nodos.

store es muy común en rutas calientes, por lo que probablemente aún sea bastante costoso.

Para este caso, ¿podríamos especificar siempre el valor del signo? ¿Así que solo hazlo 1 si se coloca en la memoria?

@wanderer Eso es esencialmente lo que implica la canonicalización: verifique si el valor es un NaN y, de ser así, aplique alguna corrección. Por lo general, es una comparación adicional y una bifurcación en la ruta activa.

También debo agregar, no he comparado ninguna de las opciones mencionadas en este número; cualquier evaluación comparativa que cualquiera pueda agregar aquí sería bienvenida.

Me inclinaría fuertemente hacia el nivel actual de no determinismo, que favorece el desempeño.
De hecho, me sorprendió un poco que wasm defina rigurosamente los bits de mantisa de NaN.
Si bien esto puede ser suficiente para los procesadores de hoy, quién sabe qué procesadores futuros vendrán.

Si el wasm se genera a partir de un lenguaje de alto nivel, ese traductor podría proporcionar una opción para controlar el nivel de semántica FP, similar a -ffast-math, por ejemplo. Luego, el traductor podría insertar cualquier corrección adicional de wasm necesaria para obligar a los nans al formato deseado. Con ese fin, podríamos proporcionar operadores isNan o normalizeNan, aunque no estoy abogando por esto ahora.

En resumen, "arreglar" esto es mejor dejarlo a una herramienta de nivel superior, en mi opinión.

+1 @mbodart. Editar : oh, mira, en realidad hay una cosa +1 ahora :). Por cierto, personalmente creo que Wasm = dominación mundial y que, por lo tanto, los futuros procesadores no se opondrán a ello.

Me gustaría cuantificar los efectos de esto en el rendimiento antes de tomar una decisión. Creo que nuestras cadenas de herramientas todavía son demasiado inmaduras para realizar buenas mediciones de rendimiento en este momento (hay polos más largos en el camino de esta). Dicho de otra manera: quiero evitar "la muerte por mil cortes".

Estoy de acuerdo con @jfbastien. Los efectos del rendimiento deben cuantificarse primero antes de jugar con la semántica.

Para ser claros, actualmente no estoy abogando por un cambio aquí; Estoy recopilando material de justificación. Un poco de no determinismo sobresale y quiere una explicación. Y hasta donde yo sé, nadie ha cuantificado los efectos en el rendimiento de hacer que estos bits no sean deterministas tampoco.

ARMv8 no siempre propaga el primer operando cuando ambos operandos son NaN. La regla es:

  • Si un operando es un NaN silencioso y el otro es un NaN de señalización, propague la NaN de señalización.
  • De lo contrario, propague el primer operando.

Mientras leo las especificaciones, esto se aplica a los modos Aarch32 y Aarch64 de ARMv8.

Este comportamiento es diferente del SSE que propaga el primer operando en ambos casos.

Ambas arquitecturas convertirán un sNaN en un qNaN estableciendo el bit de silencio antes de propagarlo.

@stoklund ¡ Buen lugar! Me perdí que ARM elegir el primer NaN no sucede en el caso en el que el segundo NaN está señalando. Eso complicaría la estrategia que expuse para el caso de NaN múltiple anterior, por lo que podemos mencionarlo en la justificación.

Ahora he creado el número 973 para proponer un texto específico que resuma lo anterior.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

badumt55 picture badumt55  ·  8Comentarios

spidoche picture spidoche  ·  4Comentarios

JimmyVV picture JimmyVV  ·  4Comentarios

thysultan picture thysultan  ·  4Comentarios

aaabbbcccddd00001111 picture aaabbbcccddd00001111  ·  3Comentarios