Design: Documenter pourquoi les bits NaN ne sont pas entièrement déterministes

Créé le 22 mars 2016  ·  15Commentaires  ·  Source: WebAssembly/design

(Je ne plaide pas actuellement ; c'est pour que nous prenions une décision éclairée et que nous rassemblions du matériel pour une justification.)

En regardant Nondeterminism.md , les parties de bits NaN se démarquent. Bien sûr, les threads peuvent faire la course, les ressources peuvent être épuisées et des fonctionnalités peuvent être ajoutées ; ce sont des conséquences de la conception globale. Mais des bits NaN ? Les machines virtuelles pourraient-elles être un peu plus intelligentes et s'occuper de cela ? Il y a deux problèmes :

Lorsqu'une opération obtient plus d'un opérande NaN, lequel propage-t-elle ?

IEEE 754 laisse cela non spécifié, mais au moins x86, ARM et Power choisissent tous le "premier" opérande, et c'est un choix raisonnable.

Cependant, fixer le choix au niveau wasm signifierait que les implémentations wasm ne peuvent pas commuter les additions et multiplications à virgule flottante, ce qui est parfois une optimisation utile sur les pré-VEX x86 où les instructions écrasent l'une de leurs entrées. En outre, il faudrait que toute personne utilisant une machine virtuelle basée sur LLVM lui apprenne que l'addition et la multiplication ne sont pas commutatives sur wasm.

On pourrait raisonnablement affirmer que ce ne sont pas des obstacles, donc ce problème est théoriquement réparable s'il y a un fort désir.

Lorsqu'une opération produit un NaN et n'a pas d'opérande NaN, quel est le bit de signe ?

x86 utilise 1, ARM utilise 0.

Le moyen le plus simple de résoudre ce problème serait de canoniser après chaque opération en virgule flottante. C'est faisable, bien qu'une difficulté réside dans le fait que 0 et 1 sont des valeurs valides possibles lorsqu'il y a un opérande NaN à propager, il ne suffirait donc pas de simplement vérifier un résultat NaN et de le canoniser ; il faudrait vérifier un résultat NaN et un manque d'opérandes NaN, et ensuite seulement canoniser.

La canonisation après _chaque_ opération serait très coûteuse ; une autre option consiste à simplement canoniser aux points d'"échappement" des calculs (le chemin de canonisation devrait alors essentiellement rejouer tout un flux de calcul afin de déterminer la sortie NaN correcte). Il s'agit d'une amélioration, mais qui ajoute probablement encore une quantité importante de frais généraux.

Une autre implémentation possible serait de démasquer l'exception invalide, de prendre un piège chaque fois qu'un NaN est généré, puis d'effectuer la canonisation et le retour. Les inconvénients incluent l'utilisation d'un mode CPU autre que celui par défaut et le fait d'être très lent s'il y a beaucoup d'invalides générés.

Malheureusement, ces approches comportent des inconvénients importants. À moins que d'autres idées ne fassent surface, ou qu'il y ait un désir très fort, cela semble difficile à résoudre.


Une autre chose à noter est que les bits NaN sont difficiles à observer accidentellement, ce n'est donc pas un problème majeur de portabilité en général.

Est-ce que quelqu'un d'autre a des idées à ajouter ?

clarification floating point

Commentaire le plus utile

J'aimerais quantifier les effets sur les performances de cela avant de prendre une décision. Je pense que nos chaînes d'outils sont encore trop immatures pour faire de bonnes mesures de performances pour le moment (il y a des pôles plus longs sur celui-ci). En d'autres termes : je veux éviter "la mort par mille coupures".

Tous les 15 commentaires

Une autre chose à noter est que les bits NaN sont difficiles à observer accidentellement

Existe-t-il un moyen de les rendre impossibles à observer ou au moins le bit de signe?

OMI, les non-déterminismes NaN devraient être laissés de côté, étant donné qu'il est si rare que les logiciels s'en préoccupent. Pourquoi sacrifier les performances pour les rendre déterministes ?

Voici une liste des façons dont on peut observer les bits NaN :

  • reinterpret conversion
  • store dans la mémoire linéaire et chargez les bits avec une interprétation différente (ou laissez les bits être observés de l'extérieur)
  • passer un argument à un call d'une fonction importée, ou renvoyer une valeur à partir d'une fonction exportée
  • copysign le bit de signe sur un non-NaN

Les machines virtuelles pourraient insérer du code de canonisation avant chacun d'entre eux ; c'est l'idée de "canoniser aux points d'échappement" discutée ci-dessus. store est très courant sur les chemins chauds, donc ce serait probablement encore assez cher.

@qwertie Je me ici, il existe des moyens de le rendre totalement déterministe même s'il n'entre pas dans la spécification.

@qwertie Les avantages peuvent inclure une portabilité légèrement supérieure (il existe du code dans le monde qui utilise imprudemment la fonction IEEE 754 totalOrder, par exemple), une reproductibilité légèrement supérieure et des invariants plus forts lors de calculs symétriques sur plusieurs nœuds.

store est très courant sur les chemins chauds, donc cela coûterait probablement encore assez cher.

Pour ce cas, pourrions-nous toujours spécifier la valeur du signe ? donc comme juste le faire 1 s'il est placé dans mem?

@wanderer C'est essentiellement ce que la

Je dois également ajouter que je n'ai évalué aucune des options mentionnées dans ce numéro ; toute analyse comparative que n'importe qui peut ajouter ici serait la bienvenue.

Je pencherais fortement pour le niveau actuel de non-déterminisme, qui favorise la performance.
En fait, j'ai été quelque peu surpris que wasm définisse rigoureusement les bits de mantisse de NaN.
Bien que cela puisse suffire pour les processeurs d'aujourd'hui, qui sait quels futurs processeurs pourraient arriver.

Si le wasm est généré à partir d'un langage de haut niveau, ce traducteur pourrait fournir une option pour contrôler le niveau de sémantique FP, similaire à -ffast-math par exemple. Le traducteur pourrait alors insérer toute correction wasm supplémentaire nécessaire pour contraindre les nans à un format souhaité. À cette fin, nous pourrions fournir des opérateurs isNan ou normalizeNan, bien que je ne préconise pas cela maintenant.

En bref, il est préférable de "réparer" cela à un outil de niveau supérieur, l'OMI.

+1 @mbodart. Edit : oh regarde il y a en fait un truc +1 maintenant :). Au fait, je pense personnellement que Wasm = domination mondiale et que les futurs processeurs ne s'y opposeront donc pas.

J'aimerais quantifier les effets sur les performances de cela avant de prendre une décision. Je pense que nos chaînes d'outils sont encore trop immatures pour faire de bonnes mesures de performances pour le moment (il y a des pôles plus longs sur celui-ci). En d'autres termes : je veux éviter "la mort par mille coupures".

Je suis d'accord avec @jfbastien. Les effets sur les performances doivent d'abord être quantifiés avant de bricoler la sémantique.

Pour être clair, je ne plaide pas actuellement pour un changement ici ; Je rassemble du matériel de justification. Le non-déterminisme du bit NaN ressort et veut des explications. Et pour autant que je sache, personne n'a quantifié les effets sur les performances de rendre ces bits non déterministes non plus.

ARMv8 ne propage pas toujours le premier opérande lorsque les deux opérandes sont NaN. La règle est :

  • Si un opérande est un NaN silencieux et l'autre est un NaN de signalisation, propagez le NaN de signalisation.
  • Sinon, propagez le premier opérande.

Au moment où je lis la spécification, cela s'applique aux modes Aarch32 et Aarch64 d'ARMv8.

Ce comportement est différent de SSE qui propage le premier opérande dans les deux cas.

Les deux architectures convertiront un sNaN en un qNaN en définissant le bit de silence avant de le propager.

@stoklund Bon endroit ! J'ai raté que ARM sélectionne le premier NaN ne se produise pas dans le cas où le deuxième NaN signale. Cela compliquerait la stratégie que j'ai présentée pour le cas de NaN multiple ci-dessus, nous pouvons donc le mentionner dans la justification.

J'ai maintenant créé #973 pour proposer un texte spécifique résumant ce qui précède.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

Artur-A picture Artur-A  ·  3Commentaires

chicoxyzzy picture chicoxyzzy  ·  5Commentaires

jfbastien picture jfbastien  ·  6Commentaires

frehberg picture frehberg  ·  6Commentaires

aaabbbcccddd00001111 picture aaabbbcccddd00001111  ·  3Commentaires