2010-05-26 11 views
5

J'ai cette boucle écrite en C++, qui compilé avec MSVC2010 prend beaucoup de temps à s'exécuter. (300ms)performance bizarre en C++ (VC 2010)

for (int i=0; i<h; i++) { 
    for (int j=0; j<w; j++) { 
     if (buf[i*w+j] > 0) { 
      const int sy = max(0, i - hr); 
      const int ey = min(h, i + hr + 1); 
      const int sx = max(0, j - hr); 
      const int ex = min(w, j + hr + 1); 
      float val = 0; 
      for (int k=sy; k < ey; k++) { 
       for (int m=sx; m < ex; m++) { 
        val += original[k*w + m] * ds[k - i + hr][m - j + hr]; 
       } 
      } 
      heat_map[i*w + j] = val; 
     } 
    } 
} 

Il me semblait un peu étrange pour moi, donc je fait quelques tests, changé quelques bits à l'assembleur en ligne: (plus précisément, le code qui résume "val")

for (int i=0; i<h; i++) { 
    for (int j=0; j<w; j++) { 
     if (buf[i*w+j] > 0) { 
      const int sy = max(0, i - hr); 
      const int ey = min(h, i + hr + 1); 
      const int sx = max(0, j - hr); 
      const int ex = min(w, j + hr + 1); 
      __asm { 
       fldz 
      } 
      for (int k=sy; k < ey; k++) { 
       for (int m=sx; m < ex; m++) { 
        float val = original[k*w + m] * ds[k - i + hr][m - j + hr]; 
        __asm { 
         fld val 
         fadd 
        } 
       } 
      } 
      float val1; 
      __asm { 
       fstp val1 
      } 
      heat_map[i*w + j] = val1; 
     } 
    } 
} 

Maintenant, il fonctionne dans la moitié du temps, 150ms. Il fait exactement la même chose, mais pourquoi est-il deux fois plus rapide? Dans les deux cas, il a été exécuté en mode Release avec des optimisations activées. Est-ce que je fais quelque chose de mal dans mon code C++ original?

+3

Avez-vous essayé de comparer le code d'assemblage généré dans les deux cas ... –

Répondre

5

Je vous suggère d'essayer différents modèles de calcul à virgule flottante pris en charge par le compilateur - precise, strict ou fast (voir option /fp) - avec votre code d'origine avant de faire des conclusions. Je soupçonne que votre code d'origine a été compilé avec un modèle à virgule flottante trop restrictif (pas suivi par votre assemblage dans la deuxième version du code), ce qui explique pourquoi l'original est beaucoup plus lent. En d'autres termes, si le modèle original était en effet trop restrictif, alors vous étiez simplement en train de comparer des pommes à des oranges. Les deux versions n'ont pas vraiment fait la même chose, même si cela pouvait sembler à première vue. Notez, par exemple, que dans la première version du code, la somme intermédiaire est accumulée dans une valeur float. Si elle était compilée avec le modèle precise, les résultats intermédiaires devraient être arrondis à la précision du type float, même si la variable val était optimisée et que le registre FPU interne était utilisé à la place. Dans votre code de montage, vous ne prenez pas la peine d'arrondir le résultat accumulé, ce qui aurait pu contribuer à améliorer ses performances.

Je vous suggère de compiler les deux versions du code en mode /fp:fast et de voir comment leurs performances se comparent dans ce cas.

+0

Merci!J'ai exécuté mon code original en mode rapide et il fonctionne maintenant en 80ms, alors que la 2ème version fonctionne encore à 150ms en mode rapide, donc je suppose que le compilateur sait encore mieux :) J'ai trouvé ces # pragma pour MSVC pour changer la précision du flotteur par fonction (ne fonctionne pas de fonctions à l'intérieur): #pragma float_control (précis, au large, push) ... code ici ... #pragma float_control (pop) Mais plus précisément: http://msdn.microsoft.com/en-us/library/45ec64h6(VS.80).aspx – raicuandi

3

Quelques choses à vérifier:

  • Vous devez vérifier que est en fait est le même code. Comme dans, vos instructions d'assemblage en ligne sont-elles exactement les mêmes que celles générées par le compilateur? Je peux voir trois différences potentielles (potentiel car ils peuvent être optimisés). Le premier est le réglage initial de val à zéro, le second est la variable supplémentaire val1 (improbable car il ne changera probablement que la soustraction constante du pointeur de la pile), le troisième est que votre version d'assemblage en ligne peut ne pas mettre les résultats intermédiaires retour au val.

  • Vous devez vous assurer que votre espace d'échantillonnage est grand. Vous n'avez pas mentionné si vous n'aviez fait qu'un seul passage de chaque version ou une centaine de fois, mais plus vous courrez, mieux c'est, afin de supprimer l'effet de "bruit" dans vos statistiques.

  • Une mesure encore meilleure serait le temps CPU plutôt que le temps écoulé. Le temps écoulé est soumis à des changements environnementaux (comme votre antivirus ou l'un de vos services décidant de faire quelque chose au moment où vous testez). Le grand espace d'échantillonnage atténuera, mais ne résoudra pas nécessairement, ceci.