web-dev-qa-db-fra.com

Opérateur Math.Pow vs multiply (performance)

Tout le monde sait si l'opérateur multiplier est plus rapide que d'utiliser la méthode Math.Pow? Comme:

n * n * n

contre

Math.Pow ( n, 3 )
34
Joan Venge

Fondamentalement, vous devriez benchmark pour voir.

Deviner éduqué (peu fiable):

Au cas où il ne serait pas optimisé pour la même chose par un compilateur ...

Il est très probable que x * x * x Soit plus rapide que Math.Pow(x, 3) car Math.Pow Doit traiter le problème dans son cas général, traitant des puissances fractionnaires et d'autres problèmes, tandis que x * x * x Prendrait juste quelques instructions de multiplication, donc il est très probable que ce soit plus rapide.

32
Mehrdad Afshari

Je viens de réinstaller Windows, donc Visual Studio n'est pas installé et le code est moche

using System;
using System.Diagnostics;

public static class test{

public static void Main(string[] args){
    MyTest();
    PowTest();
}

static void PowTest(){
    var sw = Stopwatch.StartNew();
    double res = 0;
    for (int i = 0; i < 333333333; i++){
        res = Math.Pow(i,30); //pow(i,30)
    }
    Console.WriteLine("Math.Pow: " + sw.ElapsedMilliseconds + " ms:  " + res);
}

static void MyTest(){
    var sw = Stopwatch.StartNew();
    double res = 0;
    for (int i = 0; i < 333333333; i++){
        res = MyPow(i,30);
    }
    Console.WriteLine("MyPow: " + sw.ElapsedMilliseconds + " ms:  " + res);
}



static double MyPow(double num, int exp)
{
    double result = 1.0;
    while (exp > 0)
    {
        if (exp % 2 == 1)
            result *= num;
        exp >>= 1;
        num *= num;
    }

    return result;
}
}

Les resultats:
csc/o test.cs

test.exe

MyPow: 6224 ms:  4.8569351667866E+255  
Math.Pow: 43350 ms:  4.8569351667866E+255 

Exponentiation par quadrature (voir https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int ) est beaucoup plus rapide que Math.Pow dans mon test (mon CPU est un Pentium T3200 à 2 Ghz)

EDIT: La version .NET est 3.5 SP1, OS est Vista SP1 et le plan d'alimentation est de haute performance.

42
ggf31416

Quelques règles de base de plus de 10 ans d'optimisation dans le traitement d'image et le calcul scientifique:

Les optimisations à un niveau algorithmique ont battu n'importe quelle quantité d'optimisation à un faible niveau. Malgré la sagesse conventionnelle "Écrivez l'évidence, puis optimisez", cela doit être fait au début. Pas après.

Les opérations mathématiques codées à la main (en particulier les types SIMD SSE +) surclasseront généralement celles qui sont entièrement vérifiées et généralisées.

Toute opération où le compilateur sait à l'avance ce qui doit être fait est optimisée par le compilateur. Celles-ci incluent: 1. Les opérations de mémoire telles que Array.Copy () 2. Pour les boucles sur des tableaux où la longueur du tableau est donnée. Comme pour (..; i<array.Length;..)

Fixez-vous toujours des objectifs irréalistes (si vous le souhaitez).

8
Doug

Il se trouve que je l'ai testé hier, puis j'ai vu votre question maintenant.

Sur ma machine, un Core 2 Duo exécutant 1 thread de test, il est plus rapide d'utiliser la multiplication jusqu'à un facteur 9. À 10, Math.Pow (b, e) est plus rapide.

Cependant, même avec un facteur 2, les résultats ne sont souvent pas identiques. Il y a des erreurs d'arrondi.

Certains algorithmes sont très sensibles aux erreurs d'arrondi. J'ai dû littéralement exécuter plus d'un million de tests aléatoires jusqu'à ce que je découvre cela.

7
IamIC

C'est tellement micro que vous devriez probablement le comparer à des plates-formes spécifiques, je ne pense pas que les résultats pour un Pentium Pro seront nécessairement les mêmes que pour un ARM ou Pentium II.

Dans l'ensemble, il est fort probable que cela soit totalement hors de propos.

4
Henk Holterman

J'ai vérifié et Math.Pow() est défini pour prendre deux doubles. Cela signifie qu'il ne peut pas faire de multiplications répétées, mais doit utiliser une approche plus générale. S'il y avait une Math.Pow(double, int), elle pourrait probablement être plus efficace.

Cela étant dit, la différence de performances est presque certainement absolument triviale, et vous devez donc utiliser ce qui est le plus clair. Les micro-optimisations comme celle-ci sont presque toujours inutiles, peuvent être introduites à tout moment et doivent être laissées pour la fin du processus de développement. À ce stade, vous pouvez vérifier si le logiciel est trop lent, où se trouvent les points chauds et passer vos efforts de micro-optimisation là où cela fera réellement la différence.

4
David Thornley

Utilisons la convention x ^ n. Supposons que n est toujours un entier.

Pour les petites valeurs de n, la multiplication ennuyeuse sera plus rapide, car Math.Pow (probablement, dépend de l'implémentation) utilise des algorithmes sophistiqués pour permettre à n d'être non intégral et/ou négatif.

Pour les grandes valeurs de n, Math.Pow sera probablement plus rapide, mais si votre bibliothèque n'est pas très intelligente, elle utilisera le même algorithme, ce qui n'est pas idéal si vous savez que n est toujours un entier. Pour cela, vous pouvez coder une implémentation de exponentiation par quadrature ou un autre algorithme sophistiqué.

Bien sûr, les ordinateurs modernes sont très rapides et vous devriez probablement vous en tenir à la méthode la plus simple, la plus facile à lire, la moins susceptible d'être boguée jusqu'à ce que vous testiez votre programme et soyez sûr que vous obtiendrez une accélération significative en utilisant un algorithme différent.

2
David

Math.Pow(x, y) est généralement calculé en interne comme Math.Exp(Math.Log(x) * y). Chaque équation de puissance nécessite de trouver un logarithme naturel, une multiplication et d'élever e à une puissance.

Comme je l'ai mentionné dans ma réponse précédente, ce n'est qu'à une puissance de 10 que Math.Pow() devient plus rapide, mais la précision sera compromise si vous utilisez une série de multiplications.

0
IamIC

Je ne suis pas d'accord pour dire que les fonctions intégrées à la main sont toujours plus rapides. Les fonctions cosinus sont bien plus rapides et plus précises que tout ce que je pourrais écrire. Quant à pow (). J'ai fait un test rapide pour voir à quel point Math.pow () était lent en javascript, car Mehrdad a mis en garde contre les conjectures

    for (i3 = 0; i3 < 50000; ++i3) { 
      for(n=0; n < 9000;n++){ 
        x=x*Math.cos(i3);
      }
    }

voici les résultats:

Each function run 50000 times 

time for 50000 Math.cos(i) calls = 8 ms 
time for 50000 Math.pow(Math.cos(i),9000) calls = 21 ms 
time for 50000 Math.pow(Math.cos(i),9000000) calls = 16 ms 
time for 50000 homemade for loop calls 1065 ms

si vous n'êtes pas d'accord, essayez le programme sur http://www.m0ose.com/javascripts/speedtests/powSpeedTest.html

0
dooder duderama