Des processeurs ont-ils déjà implémenté une instruction de racine carrée entière ?
(retrocomputing.stackexchange.com)- La question ne porte pas sur la classique instruction
sqrten virgule flottante, mais sur l’existence de cas où une racine carrée entière était fournie comme instruction CPU ou fonctionnalité matérielle ; le divider/square-rooter de la Nintendo DS est similaire, mais ce n’est pas une instruction native - Le Forth CPU Harris RTX 2000 et sa version durcie militaire RTX 2010 sont cités comme des exemples proposant une instruction de racine carrée en plusieurs étapes ; sur le RTX 2000, on obtenait le résultat avec 1 instruction de préparation puis 15 instructions d’itération
- Cas encore plus ancien, ENIAC disposait dès 1946 d’une unité divider/square-rooter pilotant des accumulateurs entiers décimaux, capable d’effectuer jusqu’à 40 divisions par seconde ou 3 opérations de racine carrée par seconde
- La racine carrée entière exige un multiplicateur entier rapide et une précision suffisante, ce qui représentait une charge importante pour les CPU historiques ; il existe aussi des approches séparant estimation et itération, comme
frsqrte/frsqrtssur ARMv8, pour ajuster le compromis précision/vitesse - La méthode de l’inverse square root popularisée par Quake n’offre plus en général d’avantage de performance sur le matériel moderne ; recherche dans une table, interpolation, itérations de type Halley ou divide and conquer en virgule fixe restent des options selon l’environnement d’implémentation
Portée de la question et cas de la Nintendo DS
- La question porte sur l’existence de processeurs ayant réellement implémenté une instruction de racine carrée entière
- Les instructions de racine carrée en virgule flottante sont courantes, mais le point de départ est que l’auteur de la question n’avait jamais vu d’instruction dédiée à la racine carrée entière
- La Nintendo DS possédait un integer divider/square rooter mappé en mémoire
- Cela aidait aux calculs 3D, les processeurs ARM n’ayant ni FPU ni diviseur matériel
- Mais le fait important est que ce n’était pas une instruction processeur native
Harris RTX 2000 et RTX 2010
- Le Forth CPU Harris RTX 2000 est mentionné comme un exemple fournissant une instruction de racine carrée en plusieurs étapes
- Son équivalent durci militaire, le RTX 2010, proposait la même famille de fonctionnalités
- Une ressource associée est Stack Computers: RTX 2000
- D’après le RTX2000 Family Programmer’s Reference Manual, cette fonction ressemble davantage à une racine carrée itérative, avec 1 instruction de préparation suivie de 15 instructions d’itération pour obtenir la valeur finale
- Le texte de Ken Lyons, “A Fast Method for Finding an Integer Square Root”, est aussi cité comme source décrivant l’implémentation matérielle et des exemples de programmation sur la famille RTX2000
L’unité divider/square-rooter de l’ENIAC
- Dès 1946, l’ENIAC constitue lui aussi un cas de matériel de racine carrée entière
- D’après la description citée, l’ENIAC pilotait 4 accumulateurs via une unité spéciale de multiplication pour effectuer jusqu’à 385 multiplications par seconde
- 5 accumulateurs étaient pilotés par une unité spéciale de division/racine carrée, capable de traiter jusqu’à 40 divisions par seconde ou 3 racines carrées par seconde
- Les accumulateurs de l’ENIAC fonctionnaient en entiers décimaux
Pourquoi la racine carrée entière est difficile à implémenter
- Une réponse explique qu’une méthode efficace consiste à calculer d’abord l’inverse de la racine carrée par itérations de Newton-Raphson, puis à multiplier par la valeur d’origine
- Cette approche est connue via la “méthode Quake”, et on en trouve aujourd’hui des formes généralisées dans CPU et GPU modernes avec une instruction d’estimation puis une instruction d’itération
- La contrainte clé de cette approche est la nécessité d’un multiplicateur rapide
- Pour une racine carrée en virgule flottante, il faut un multiplicateur flottant rapide, dont dispose une FPU
- Pour une racine carrée entière, il faut un multiplicateur entier rapide, mais historiquement la plupart des CPU ne disposaient pas d’un tel matériel
- Il est aussi précisé que, pour fournir une précision suffisante, il faut un multiplicateur rapide d’une largeur égale au double de celle de l’entrée
- Comme le niveau de précision requis n’est pas toujours le même, séparer estimation et itération comme avec
frsqrteetfrsqrtspermet d’ajuster le nombre d’itérations selon le compromis vitesse/précision recherché
Débat sur la technique de Quake et les implémentations modernes de sqrt
- Une autre réponse conteste l’idée que l’astuce de Quake soit la plus efficace : ce n’est plus vrai depuis longtemps, sauf éventuellement pour produire un résultat flottant de faible qualité sur un matériel particulier
- Sur les puces modernes, une instruction
sqrtnative est bien plus rapide, parfois en quelques cycles d’horloge seulement - Parmi les approches plus rapides proposées figure le stockage d’une table de valeurs à espacement non uniforme, la récupération rapide de deux valeurs puis leur interpolation, le décalage de l’exposant en base 2, puis si nécessaire une itération, potentiellement meilleure que Newton-Raphson
- Les méthodes de type Halley et d’autres schémas itératifs peuvent converger plus vite que Newton-Raphson, mais la vitesse réelle dépend du coût de chaque opération
- Pour un domaine strictement entier, par exemple
2^32, on peut appliquer les mêmes idées en virgule fixe- Une méthode matérielle simple de type divide and conquer est proposée
- Chaque bloc de 8 bits est mappé sur une table de 256 valeurs en virgule fixe, consultée en parallèle ; puis, avec 3 multiplications dont 2 en parallèle, on peut obtenir une valeur sur 32 bits avant troncature
- La recherche sur l’optimisation de
sqrtcontinue, et un document INRIA HAL est cité comme exemple
1 commentaires
Avis de Hacker News
AArch64 NEON dispose de l’instruction URSQRTE, ce qui est plus proche de la question initiale qu’on pourrait le croire
Si l’on voit une valeur 32 bits comme un entier en virgule fixe avec 32 bits de partie fractionnaire, l’intervalle représenté va de 0 à 1-ε avec un pas uniforme, où ε=2^-32
URSQRTE calcule une approximation de la racine carrée inverse, puis la divise par deux, et borne le résultat dans l’intervalle de 0 à 1-ε
Un entier en virgule fixe n’est pas strictement un entier, et une racine carrée inverse approximative n’est pas une racine carrée, mais on arrive tout de même assez près
L’instruction apparentée FRSQRTE est beaucoup plus générale et fournit une approximation de la racine carrée inverse pour les nombres à virgule flottante 32 bits
Est-ce possible en un seul cycle d’horloge ? Oui, avec une très grande table de consultation
On pourrait sans doute réduire sa taille selon la quantité de portes logiques en série que l’on peut faire passer dans un cycle d’horloge
Par exemple, la racine carrée binaire de 10000 ressemble beaucoup à celle de 100, seule la quantité de zéros change
frsqrte) sont généralement implémentées avec ce type de consultation de table, indexée par une partie des bits de la mantisse et par le bit de poids faible de l’exposantLa précision est en gros comparable à celle du bf16 (ARM, RISC-V) ou du fp16 (x86) ; si l’on a besoin de davantage de précision, on effectue ensuite quelques itérations de Newton-Raphson
À chaque étape, on calcule s’il faut définir un nouveau bit dans le résultat
n_oldvian2_new = (n_old + (1 << bit))^2 = n2_old + (n_old << (bit + 1)) + (1 << (bit*2))On compare ensuite avec l’opérande d’origine et, si c’est inférieur ou égal, 1) on positionne le bit dans le résultat et 2) on met à jour
n2_oldavecn2_newAvec un jeu d’instructions microcodées et une ALU adaptés, c’est faisable en n/2, voire peut-être n cycles d’horloge, et avec plus d’optimisation on peut réduire n à l’indice du bit le plus à gauche positionné dans l’opérande
Une grande table de consultation devrait être récupérée en mémoire, non ? Il y aurait alors de la latence due au cache et à la hiérarchie mémoire
Cela revient à stocker N^2 pour chaque réponse N
C’est réalisable pour des entiers 16 bits, peut-être pour 32 bits, mais pas pour 64 bits
Si l’on élargit la définition de « processeur » aux dispositifs électromécaniques, le Friden SRQ pouvait calculer des racines carrées uniquement avec additions et décalages, sans aucun composant électronique hormis le moteur
Il fallait régler manuellement la position de la virgule décimale, donc techniquement on peut aussi parler d’opérations entières
Vidéo : https://youtu.be/o44a1ao5h8w
Avec la suite
1 + 3 + 5 + ... + 2k + 1, ne peut-on pas obtenir la racine carrée entière de n’importe quel entier ?En gros, cela consiste à trouver le k du terme le plus proche de cette suite qui soit inférieur ou égal à mon nombre
Par définition, c’est bien un algorithme, mais implémenté naïvement il est très lent même pour des nombres 32 bits
À ce niveau-là, autant faire une recherche binaire, ce sera bien plus rapide
(x+y)^2=x^2+2xy+y^2avec l’observation que, dans n’importe quelle base, la racine carrée d’un nombre à 2n chiffres a au plus n chiffresC’est la même méthode courante que le calcul manuel de racine carrée au papier-crayon
Si on traite les données par blocs de 8 bits, il suffit d’une table de consultation pour la racine carrée des nombres sur 8 bits
Ce passage dans une réponse plus bas m’a fait rire :
Il faut descendre un peu dans les réponses pour le lire, mais le fait que la réponse soit ENIAC est vraiment drôle
Il suffit de lire un peu pour constater l’inverse
La plupart des idées intelligentes d’aujourd’hui étaient déjà utilisées dans les ordinateurs des années 1940 à 1960, et sont réutilisées dans les nouvelles puces à semi-conducteurs
C’est le cas du pipeline, de l’exécution dans le désordre ou du multicœur
Le matériel ancien était peut-être un peu « rustique », mais son architecture employait des techniques très ingénieuses
2 ^ (1/2 * Log2(X)) = sqrt(X)Si l’on remplace
Log2(x)par le comptage des zéros en tête, on obtient une approximation vraiment grossièreEn approximant mieux
Log(2), on peut se rapprocher davantage de la réponseSi l’on veut seulement une approximation très grossière et non la réponse exacte à l’entier le plus proche, il suffit de faire un décalage à droite de la moitié de la position du premier bit à 1
Presque tous les processeurs ont une instruction de décalage, et les instructions du type FLO (Find Leading One) ou FFS (Find First Set) semblent si courantes que je ne sais même pas à quel point elles peuvent manquer
Dans certains usages, une approximation aussi grossière peut être aussi utile qu’une réponse exacte
Par exemple lorsqu’il suffit d’une bonne valeur initiale pour des itérations de Newton-Raphson ensuite
Bien sûr, l’astuce du décalage à droite convient aussi comme valeur initiale pour un calcul plus précis de racine carrée :P
C’est désormais une histoire Internet assez célèbre, avec Carmack et un nombre magique sur 32 bits
Une autre fonction intrinsèque matérielle CUDA que j’aime bien personnellement est
log2Si ma mémoire est bonne, la plupart, voire tous les DSP en virgule fixe, ont une instruction de racine carrée ou une instruction auxiliaire
Une analyse exhaustive d’algorithmes de racine carrée, à moitié pertinente et susceptible d’intéresser les fans du 6502 : https://github.com/TobyLobster/sqrt_test