11
votes

Pourquoi le programme C ++ compilé pour la plate-forme X64 est-il plus lent que compilé pour X86?

J'ai écrit un programme et l'a compilée pour la plate-forme X64 et X86 dans Visual Studio 2010 sur Intel Core I5-2500. La version X64 prend environ 19 secondes pour l'exécution et X86 prend environ 17 secondes. Que peut être la raison d'un tel comportement?

#include "timer.h"

#include <vector>
#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>

/********************DECLARATIONS************************************************/
class Vector
{
public:
    Vector():x(0),y(0),z(0){}

    Vector(double x, double y, double z)
        : x(x)
        , y(y)
        , z(z)
    {
    }

    double x;
    double y;
    double z;
};


double Dot(const Vector& a, const Vector& b)
{
    return a.x * b.x + a.y * b.y + a.z * b.z;
}


class Vector2
{
public:
    typedef double value_type;

    Vector2():x(0),y(0){}

    Vector2(double x, double y)
        : x(x)
        , y(y)
    {
    }

    double x;
    double y;
};

/******************************TESTS***************************************************/

void Test(const std::vector<Vector>& m, std::vector<Vector2>& m2)
{
    Vector axisX(0.3f, 0.001f, 0.25f);
    Vector axisY(0.043f, 0.021f, 0.45f);

    std::vector<Vector2>::iterator i2 = m2.begin();

    std::for_each(m.begin(), m.end(),
        [&](const Vector& v)
    {
        Vector2 r(0,0);
        r.x = Dot(axisX, v);
        r.y = Dot(axisY, v);

        (*i2) = r;
        ++i2;
    });
}


int main()
{
    cpptask::Timer timer;

    int len2 = 300;
    size_t len = 5000000;
    std::vector<Vector> m;
    m.reserve(len);
    for (size_t i = 0; i < len; ++i)
    {
        m.push_back(Vector(i * 0.2345, i * 2.67, i * 0.98));
    }

    /***********************************************************************************/
    {
        std::vector<Vector2> m2(m.size());
        double time = 0;
        for (int i = 0; i < len2; ++i)
        {
            timer.Start();
            Test(m, m2);
            time += timer.End();
        }
        std::cout << "Dot product double - " << time / len2 << std::endl;
    }
    /***********************************************************************************/


    return 0;
}


2 commentaires

Intéressant. Je suis capable de reproduire cela sur un noyau i7 920.


Il convient de noter que vous pouviez utiliser XMM intrinsics et économiser beaucoup plus de temps.


3 Réponses :


-2
votes

64 bits normalement est un peu plus lent que 32 bits (pour le code qui ne tire spécifiquement pas parti des caractéristiques de 64 bits). Un problème particulier est que les pointeurs sont plus grands, réduisant ainsi la quantité qui peut être maintenue dans le cache.


4 commentaires

Cela pourrait être vrai, mais où sont les pointeurs ici? Je vois beaucoup d'accès à la mémoire au grand vecteur et aux points de vecteur et flottant. Je ne vois pas beaucoup de bande passante de la mémoire étant consommé des pointeurs de passage.


Mais pourquoi cet article MSDN. Microsoft.com/en-us/Library/Windows/desktop/... Dites que l'architecture X64 a amélioré les opérations de point flottant.


L'ISA X86_64 comprend SSE + SSE2, qui ne peut pas être dit de x86. Par conséquent, des fichiers binaires générés avec des instructions de dénominateur commun les plus basses uniquement, sans aucune détection artisanale à la main et sans détection de CPUID et des blocs insnés séparés pour chaque plastique - et c'est probablement ce que Microsoft se réfère - est plus lent, car ils doivent se passer sans utiliser SSE.


Je ne ferais pas de déclarations de couverture sur l'avantage / l'inconvénient de X64 vs. x86. Bien sûr, pour le code limité de mémoire pouvant faire une différence, mais d'autre part x64 présente des instructions supplémentaires parfois utiles et - qui est généralement utile - plus de registres. Tout à fait sans importance pour le code FP cependant.



21
votes

Réponse courte: c'est un compilateur hoquet. x64 Optimiseur échoue.


réponse longue:

Cette version x86 est très lente si SSE2 est désactivé. Mais je suis capable de reproduire les résultats avec SSE2 activé en X86.

Si vous plongez dans l'assemblage de cette boucle la plus intérieure. La version x64 comporte deux copies de mémoire supplémentaires à la fin.

x86: xxx

x64: xxx

La version x64 a beaucoup plus (inexpliquée) se déplace à la fin de la boucle. Il ressemble à une sorte de copie de données mémoire à mémoire.

EDIT:

Il s'avère que l'optimiseur X64 n'est pas en mesure d'optimiser la copie suivante: < / p> xxx

C'est pourquoi la boucle interne a deux copies de mémoire supplémentaires. Si vous changez de boucle à ceci: xxx

ceci élimine les copies. Maintenant, la version x64 est aussi rapide que la version x86: xxx

Leçon apprise: compilateurs ne sont pas parfaits.


8 commentaires

Je ne pense pas que ce soit ... mais est "double" 64 bits si compilé pendant 64 bits contre 32 bits si compilé pour une arche 32 bits. Je crois que la taille des changements longs, mais pas sûr de si le double fait. Je le vérifierais, mais Visual Studio ne me permettrait que de compiler 32 bits aujourd'hui. Double devrait être 64 bits sur les deux (8 octets).


Nah, double est la double précision standard IEEE sur x86. L'assemblée ici est assez claire, c'est tout SSE scalaire de double précision.


Cela ressemble au même problème: Dreamincode.net/forums/topic/ 127989-Compiler-Made-Optimizati OnS Pour le réparer, ils ont utilisé l'optimisation du compilateur / O2 qui a abouti à la version 64 bits étant plus rapide que la version 32 bits. Pouvez-vous essayer cela et voir si cela aide?


J'ai utilisé / O2 Optimisation pour mes tests (ce drapeau est activé par défaut pour la version de version dans MSVC).


Mes deux tests ont également été effectués avec / o2 . Mais je pense que je vois le problème maintenant. Le compilateur X64 n'est pas en mesure d'optimiser complètement l'appel de la fonction sur point () . La valeur de retour pour les deux appels vers dot () est en cours de copie via la mémoire plutôt que d'enregistrer ... étrange. X86 n'a pas ce problème.


Cela pourrait-il également expliquer pourquoi une application Windows compilée pour 32 bits fonctionne un peu plus lentement sur une machine de 64 bits?


@Jimfell Dans cet exemple particulier, la version 32 bits utilise la FPU par défaut. Le FPU est généralement plus lent que SSE. Toutes les machines X64 ont SSE. Il est donc activé par défaut sur X64.


x86 Les conventions d'appel sont différentes. En particulier avec l'utilisation de registres pour passer des arguments. Un bon lien / optimiseur ferait cela un non-problème, mais il est clair que les conventions d'appel standard x86 facilitent la vue de cette optimisation.



0
votes

Je ne réponds pas à votre question, mais je pense qu'il vaut la peine de mentionner:

Vous ne devriez pas écrire de classes de vecteurs par vous-même. Pour des vecteurs de longueur fixe, utilisez plutôt Boost :: Array ou CV :: Vec2D et CV :: Vec3D qui a construit dans DOT et autre Fonctions rapides comme fonctionner +, - etc ... (également CV :: Vec est proposée)


0 commentaires