Je dirais que c'est un peu la raison d'être des compilateurs : tenir compte de la cible. Même si un travail manuel serait meilleur, il est infiniment plus long qu'une compilation.
C'est même pour cela que la libm est avant tout codée en C avec de très bonnes performances. Le passage à l'assembleur n'est fait que pour certaines architectures et que pour certaines fonctions. Réussir à faire mieux que le compilateur est même souvent très difficile.
Par contre, il n'y a pas de magie, le compilateur ne trouvera pas tout seul la bonne réduction et la bonne approximation. L'humain reste indispensable à ce niveau là. (pour l'instant en tout cas)
Concernant la précision, comme la définis-tu ? En relatif uniquement ? Avec une petit touche d'absolue pour gérer autour de zéro ?
De manière générale et pour la libm, la précision est définie par le standard IEEE754. En gros ça veux dire que tu dois renvoyer un résultat identique à ce que donnerais le calcul exact puis arrondis de manière à être représentable.
La ou c'est chiant c'est qu'il y a différent mode d'arrondis...
Dans mon cas, l'optimisation ce justifiais car mon code passait près de 46% de son temps à calculer des exp() et des log(). Le truc c'est que je n'avais pas besoin de gérer les nombres démoralisés, un seul type de NaN, pas d'infinis en entrée et surtout les valeurs d'entrée ne pouvait couvrir qu'un intervalle plus réduit.
Donc dans ce cas là, même avec une précision complète, on gagne déjà beaucoup : presque un facteur x2 sur les deux fonctions.
Pour ce qui est de la précision, une partie du code avais besoin de la précision complète, c'est elle qui garde le code FPU avec ses 80bits et qui fait le calcul en long double. Par contre, une grosse partie du code travail sur des doubles mais peut tolérer environs 3bits d'erreurs. Cette portions utilise le code SSE2 avec un polynôme tronqué qui fournis juste ce dont le code à besoin et permet en plus de profiter du SIMD. Au final les exp() et log() ne représentent plus que 6% du temps de calcul.
Par contre, avant de jouer avec ces fonctions il faut bien faire attention et bien mettre des gros warning. Dans mon cas, si tu leur donnes des valeurs qu'elles n'attendent pas, elles te renvoie du bruit. Leur utilité est donc très liée à mon programme. Dans le cas général ou tu dois respecter le standard IEEE 754 tu n'auras jamais de tels gains. Une bonne fonction exp() à un gain non-négligeable par rapport à la version FPU pour le cas général, par contre, si l'entrée ou la sortie est un NaN ou un Inf, ce n'est souvent pas le cas.
Pour les nombre dénormalisé, c'est souvent kifkif car la FPU à une pénalité de près de 200 pour ces cas là elle aussi.
[^] # Re: À propos de SSE et AVX
Posté par beagf . En réponse à la dépêche Intel Sandy Bridge et Linux : état des lieux. Évalué à 3.
C'est même pour cela que la libm est avant tout codée en C avec de très bonnes performances. Le passage à l'assembleur n'est fait que pour certaines architectures et que pour certaines fonctions. Réussir à faire mieux que le compilateur est même souvent très difficile.
Par contre, il n'y a pas de magie, le compilateur ne trouvera pas tout seul la bonne réduction et la bonne approximation. L'humain reste indispensable à ce niveau là. (pour l'instant en tout cas)
De manière générale et pour la libm, la précision est définie par le standard IEEE754. En gros ça veux dire que tu dois renvoyer un résultat identique à ce que donnerais le calcul exact puis arrondis de manière à être représentable.
La ou c'est chiant c'est qu'il y a différent mode d'arrondis...
Dans mon cas, l'optimisation ce justifiais car mon code passait près de 46% de son temps à calculer des exp() et des log(). Le truc c'est que je n'avais pas besoin de gérer les nombres démoralisés, un seul type de NaN, pas d'infinis en entrée et surtout les valeurs d'entrée ne pouvait couvrir qu'un intervalle plus réduit.
Donc dans ce cas là, même avec une précision complète, on gagne déjà beaucoup : presque un facteur x2 sur les deux fonctions.
Pour ce qui est de la précision, une partie du code avais besoin de la précision complète, c'est elle qui garde le code FPU avec ses 80bits et qui fait le calcul en long double. Par contre, une grosse partie du code travail sur des doubles mais peut tolérer environs 3bits d'erreurs. Cette portions utilise le code SSE2 avec un polynôme tronqué qui fournis juste ce dont le code à besoin et permet en plus de profiter du SIMD. Au final les exp() et log() ne représentent plus que 6% du temps de calcul.
Par contre, avant de jouer avec ces fonctions il faut bien faire attention et bien mettre des gros warning. Dans mon cas, si tu leur donnes des valeurs qu'elles n'attendent pas, elles te renvoie du bruit. Leur utilité est donc très liée à mon programme. Dans le cas général ou tu dois respecter le standard IEEE 754 tu n'auras jamais de tels gains. Une bonne fonction exp() à un gain non-négligeable par rapport à la version FPU pour le cas général, par contre, si l'entrée ou la sortie est un NaN ou un Inf, ce n'est souvent pas le cas.
Pour les nombre dénormalisé, c'est souvent kifkif car la FPU à une pénalité de près de 200 pour ces cas là elle aussi.