69 votes

Problème de performance : Java vs C++

J'ai toujours entendu dire que le C++ a été de façon plus efficace que Java (et c'est pourquoi la plupart des jeux sont développé en C++).

J'ai écrit un petit algorithme pour résoudre le "Huit reines puzzle" en Java et en C++, en utilisant exactement le même algorithme, puis a commencé à augmenter le nombre ou des carrés. Lorsque l'on atteint checkboards de 20*20 ou 22*22, il apparaît Java est beaucoup plus efficace (3 secondes vs 66 secondes pour le C++).

Je n'ai aucune idée pourquoi, mais je suis assez débutant en C++, donc il est possible que j'ai fait une énorme performance des erreurs, donc je l'accepte avec plaisir toute information qui pourrait m'aider à comprendre ce qui se passe.

Ci-dessous le code que j'utilise en Java:

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class HuitDames {

    /**
     * La liste des coordnnées des dames.
     */
    private static List<Point> positions = new ArrayList<>();

    /**
     * Largeur de la grille.
     */
    private static final int LARGEUR_GRILLE = 22;


    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int i = 1;
        placerDame(i);
        for (Point point : positions) {
            System.out.println("(" + point.x + "; " + point.y + ")");
        }
    }

    /**
     * Place une dame et return true si la position est bonne.
     * @param i le numéro de la dame.
     * @return si la position est bonne.
     */
    private static boolean placerDame(int i) {

        boolean bonnePosition = false;
        for (int j = 1; j <= LARGEUR_GRILLE && bonnePosition == false; j++) {
            Point emplacement = new Point(i, j);
            positions.add(emplacement);
            if (verifierPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
                bonnePosition = true;
            }
            else {
                positions.remove(i - 1);
            }
        }

        return bonnePosition;
    }

    /**
     * Vérifie que la nouvelle position n'est pas en prise avec une position déjà présente.
     * @param position la position de la nouvelle dame.
     * @return Si la position convient par rapport aux positions des autres dames.
     */
    private static boolean verifierPrise(Point position) {
        boolean nonPrise = true;
        for (Point point : positions) {
            if (!point.equals(position)) {
                // Cas où sur la même colonne.
                if (position.y == point.y) {
                    nonPrise = false;
                }
                // Cas où sur même diagonale.
                if (Math.abs(position.y - point.y) == Math.abs(position.x - point.x)) {
                    nonPrise = false;
                }
            }
        }

        return nonPrise;
    }
}

Et ci-dessous est le code en C++:

#include <iostream>
#include <list>
#include <math.h>
#include <stdlib.h>

using namespace std;


// Class to represent points.
class Point {

    private:
        double xval, yval;

    public:
        // Constructor uses default arguments to allow calling with zero, one,
        // or two values.
        Point(double x = 0.0, double y = 0.0) {
                xval = x;
                yval = y;
        }

        // Extractors.
        double x() { return xval; }
        double y() { return yval; }
};

#define LARGEUR_GRILLE 22
list<Point> positions;


bool verifierNonPrise(Point emplacement) {
    bool nonPrise = true;
    for (list<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
        if (it->x() != emplacement.x()) {
            if (it->y() == emplacement.y()) {
                nonPrise = false;
            }
            if (abs(it->y() - emplacement.y()) == abs(it->x() - emplacement.x())) {
                nonPrise = false;
            }
        }
    }

    return nonPrise;
}

bool placerDame(int i) {
    bool bonnePosition = false;
    for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
        Point emplacement(i,j);
        positions.push_back(emplacement);
        if (verifierNonPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            bonnePosition = true;
        }
        else {
            positions.pop_back();
        }
    }

    return bonnePosition;
}

int main()
{
    int i = 1;
    placerDame(i);
    for (list<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
        cout << "(" << it->x() << "; " << it->y() << ")" << endl;
    }
    return 0;
}

92voto

Brian Points 15388

en C++ est une liste liée, tandis que est un tableau. Essayez de remplacer par . Veillez également à compiler avec optimisation allumée.

89voto

Loki Astari Points 116129

Mises à jour:

C++

  • Comme l'a écrit:
    L'Échec De La Compilation
  • Remplacer les mathématiques.h => cmath
    27610 millisecondes
  • Ajoutez -O3 drapeau
    4416 millisecondes
  • Remplacer std::list avec std::vector
    2294 millisecondes
  • Remplacer le Point avec std::pair
    2384 millisecondes
  • Fait verifierNonPrise const correct
    2351 millisecondes
  • Remplacé en boucle dans verifierNonPrise avec std::find_if
    929 millisecondes
  • Remplacement double avec int (pour le rendre équivalent à Java)
    549 millisecondes

Les changements de Java

  • Comme l'écrit
    3459 millisecondes
  • Les changements verifierNonPrise retour anticipé
    368 millisecondes

Java Vs C++

> javac HuitDames.java
> time java HuitDames
real    0m0.368s
user    0m0.436s
sys     0m0.042s    
> g++ -O3 -std=c++11 HuitDames.cpp
> time ./a.out
real    0m0.541s
user    0m0.539s
sys     0m0.002s

Code Final:

#include <iostream>
#include <vector>
#include <cmath>
#include <stdlib.h>
#include <chrono>
#include <algorithm>

using namespace std;

typedef std::pair<int, int>   Point;

#define LARGEUR_GRILLE 22
vector<Point> positions;


bool verifierNonPrise(Point const& emplacement) {
    return std::find_if(positions.begin(), positions.end(), [&emplacement](Point const& val){
        if (val.first != emplacement.first) {
            if ((val.second == emplacement.second) || (abs(val.second - emplacement.second) == abs(val.first - emplacement.first))) {
                return true;
            }
        }
        return false;
    }) == positions.end();
}

bool placerDame(int i) {
    bool bonnePosition = false;

    for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
        Point emplacement(i,j);
        positions.push_back(emplacement);
        if (verifierNonPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            bonnePosition = true;
        }
        else {
            positions.pop_back();
        }
    }

    return bonnePosition;
}


int main()
{
    using std::chrono::system_clock;

    system_clock::time_point begin_time = system_clock::now();

    int i = 1;
    placerDame(i);
    for (vector<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
        cout << "(" << it->first << "; " << it->second << ")" << endl;
    }

    system_clock::time_point end_time = system_clock::now();

    long long elapsed_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - begin_time).count();
    cout << "Duration (milliseconds): "
         << elapsed_milliseconds
         << std::endl;    
}

30voto

NetVipeC Points 3167

Tester cette version, mise à jour à l'aide de C++11 caractéristiques. Testé dans GCC 4.9.0 avec -std=c++11. Testé sur Celeron 1.6 GHz, 512 MO de RAM.

Fois dans mon PC:
Original: Durée (en millisecondes): 12658
Première Version: Durée (en millisecondes): 3616
Version optimisée: Durée (en millisecondes): 1745

Les modifications sont les suivantes:

  • À l'aide de vector au lieu de list indice de Référence, et les Mots de Stroustrup.
  • À l'aide de const tout ce que nous pouvons, le compilateur est capable d'optimiser beaucoup plus s'il sait que la valeur ne change pas.
  • En utilisant std::pair au lieu de le Point.
  • À l'aide de nouveau pour boucle constante avec les itérateurs.

Source:

#include <iostream>
#include <vector>
#include <chrono>
#include <iomanip>

using namespace std;

typedef std::pair<int, int> Point;

#define LARGEUR_GRILLE 22
vector<Point> positions;

bool verifierNonPrise(const Point& emplacement) {
    bool nonPrise = true;
    for (const auto& p : positions) {
        if (p.first != emplacement.first) {
            if (p.second == emplacement.second) {
                nonPrise = false;
            }
            if (abs(p.second - emplacement.second) ==
                abs(p.first - emplacement.first)) {
                nonPrise = false;
            }
        }
    }

    return nonPrise;
}

bool placerDame(int i) {
    bool bonnePosition = false;
    for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
        Point emplacement(i, j);
        positions.emplace_back(emplacement);
        if (verifierNonPrise(emplacement) &&
            (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            bonnePosition = true;
        } else {
            positions.pop_back();
        }
    }

    return bonnePosition;
}

int main(int argc, char* argv[]) {
    std::chrono::system_clock::time_point begin_time =
        std::chrono::system_clock::now();

    positions.reserve(LARGEUR_GRILLE);

    placerDame(1);
    for (const auto& p : positions) {
        cout << "(" << p.first << "; " << p.second << ")" << endl;
    }

    std::chrono::system_clock::time_point end_time =
        std::chrono::system_clock::now();
    long long elapsed_milliseconds =
        std::chrono::duration_cast<std::chrono::milliseconds>(
            end_time - begin_time).count();
    std::cout << "Duration (milliseconds): " << elapsed_milliseconds
              << std::endl;

    return 0;
}

Certains plus profonds changements.

Les modifications comprennent:

  • De retour le plus tôt possible. Dès que la reine ne peut pas être placé.
  • Le retour à une simplification de la classe Point.
  • À l'aide de find_if algorithme pour la recherche de la reine de placement.

Source (d'une recommandation mise à jour):

#include <algorithm>
#include <iostream>
#include <vector>
#include <chrono>
#include <iomanip>

using namespace std;

struct Point {
    int x, y;
};

#define LARGEUR_GRILLE 22
vector<Point> positions;

bool verifierNonPrise(const Point& emplacement) {
    return find_if(positions.cbegin(), positions.cend(), [&emplacement](const Point& p) {
               return (p.x != emplacement.x &&
                       (p.y == emplacement.y ||
                        abs(p.y - emplacement.y) == abs(p.x - emplacement.x)));
           }) == positions.cend();
}

bool placerDame(int i) {
    for (int j = 1; j <= LARGEUR_GRILLE; j++) {
        Point emplacement{i, j};
        positions.push_back(emplacement);
        if (verifierNonPrise(emplacement) &&
            (i == LARGEUR_GRILLE || placerDame(i + 1))) {
            return true;
        } else {
            positions.pop_back();
        }
    }
    return false;
}

int main(int argc, char* argv[]) {
    std::chrono::system_clock::time_point begin_time =
        std::chrono::system_clock::now();

    positions.reserve(LARGEUR_GRILLE);

    placerDame(1);
    for (const auto& p : positions) {
        cout << "(" << p.x << "; " << p.y << ")" << endl;
    }

    std::chrono::system_clock::time_point end_time =
        std::chrono::system_clock::now();
    long long elapsed_milliseconds =
        std::chrono::duration_cast<std::chrono::milliseconds>(
            end_time - begin_time).count();
    std::cout << "Duration (milliseconds): " << elapsed_milliseconds
              << std::endl;

    return 0;
}

19voto

Durandal Points 9434

La comparaison d'un gérés dynamiquement langage compilé comme le Java d'une manière statique langage compilé comme le C++ est très difficile.

Vous serez toujours comparer des pommes à des oranges dans un sens, parce qu'ils sont conceptuellement très différents. Il commence avec l'utilisation des bibliothèques standard (ArrayList vs std::list/vecteur) qui auront potentiellement très différentes caractéristiques de performance, de même que votre code est semblable dans les deux langues.

Il y a ensuite le problème inhérent à microbenchmarks en Java (petit test en Java sont toujours plus lent, car le JIT observer le flux de programme avant qu'il ne décide quoi et comment il doit être compilé). En va de même pour les options du compilateur pour C++, même la structure du code source (indépendamment compilé et lié classes par rapport à un seul fichier source) peuvent faire une différence significative (parce qu'il modifie la quantité de "insight", le compilateur C++ a dans les autres classes).

Suivant est la différence en général dans la gestion de la mémoire, la collecte des ordures vs manuel de gestion de la mémoire (pointeurs intelligents etc. sont toujours considérés comme manuel de gestion de la mémoire).

Pour ne pas mentionner le général différences de langue, comme vous devez explicitement déclarer une méthode virtuelle en C++, alors qu'en Java , chaque membre de la méthode est virtuelle par défaut (travail si c'est vraiment virtuel au moment de l'exécution est laissé à la VM).

Avec toutes ces différences, il y aura toujours des cas où une langue aura un énorme avantage sur les autres. Un simple test avec une portée très limitée (comme votre test ici) est dit très peu de choses sur chaque langue comme un tout.

Un autre point souvent, les gens ont tendance à ignorer, c'est: Comment productive que vous pouvez être avec une langue - la vitesse n'est pas tout (regardez un succès langages de script sont dans certains domaines, en dépit d'être difficilement compétitifs lorsque l'on cherche seulement à excution de vitesse). Le manque de performance peut être écrasante, mais peut être faible productivité.

1voto

Jakub Points 505

En outre, il n’y a aucune raison d’utiliser des types float/doouble pour les coordonnées.

Vous devriez obtenir des performances si vous ne forcez pas appeler flottante point abs bibliothèque appel dans votre C++

Java stocke les coordonnées du Point en entier. Les fonctions get retournent double, cependant c’est sans doute plus facile d’optimiser loin en Java, puis en c ++.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X