Performance-Problem: Java vs C++

Habe ich immer gehört, dass C++ war viel effizienter als Java ist (und das ist der Grund, warum die meisten Spiele sind in C++ entwickelt).

Schrieb ich einen kleinen Algorithmus zur Lösung des "Acht Königinnen-Rätsel" in Java und C++, mit der exakt gleichen Algorithmus, und begann dann, Sie zu erhöhen, die Anzahl oder Quadrate.
Bei erreichen checkboards von 20*20 oder sogar 22*22, es wird angezeigt, Java ist viel effektiver (3 Sekunden vs 66 Sekunden für C++).

Ich habe keine Ahnung, warum, aber ich bin mir ziemlich Anfang mit C++, so dass es möglich ist, dass ich einige riesige performance-Fehler, so will ich gerne akzeptieren, alle Informationen, die mir helfen würden zu verstehen, was geschieht.

Unten ist der code, den ich verwenden in 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;
    }
}

Und unten ist der code, der in 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;
}
  • Hast du das kompilieren von C++-Code mit Optimierung aktiviert? Für gcc und clang hinzufügen -O3 auf der Kommandozeile.
  • verwenden Sie einen Vektor und nicht einer Liste...das Durchlaufen einer Liste in jeder Sprache ist schrecklich.
  • Erstens, nutzen std::vector<Point> statt std::list<Point>. Versuchen Sie dann, die übergabe per Referenz statt als Wert. Zum Beispiel bool verifierNonPrise(const Point& emplacement). Als Nächstes erstellen Sie nicht benannt Provisorien, sondern tun positions.emplace_back(i, j);. Es ist wahrscheinlich, es gibt viele andere Dinge, die Sie verbessern können.
  • "Ich habe immer gehört, dass C++ war viel effizienter als Java"-weil es allgemein ist.
  • Es ist überhaupt nicht ungewöhnlich für Java zu schlagen C++. Ein JITC-optimierte Java-Programm kann mehr dicht optimiert, als möglich unter Verwendung leicht Verfügbarer C++ - compiler/linker. ZB, ein JITC kann einfach inline-Operationen auf einen Punkt, während C++ können in der Regel nur tun, wenn die Funktionen deklariert sind, inlineable.
  • Als die Sprache näher an Maschinensprache (die tiefer liegenden Ebene eine Sprache ist), Sie haben mehr Knöpfe zur Steuerung Zeug und dies können Sie entweder machen oder brechen Sie Ihre code-je nachdem, wie Sie verwenden Sie diese regler. Wenn Sie nach oben schauen alle den Vergleich von Fällen, hier findest du viele Beispiele, wie schnelle C++ - code einen code, der ursprünglich langsam sehr schnell. Die meisten der Zeit, die Sie nicht tun können, die in höheren Sprachen. Dies bedeutet nicht, dass alle C++ - codes gehen, um schneller als JAVA, PYTHON ... es bedeutet nur, Sie machen den code schneller.
  • Nur ein kleiner Punkt, der wahrscheinlich nicht auf die Leistung sehr viel. Wenn alle Ihre point-Klasse hat halt x und y, und es gibt keine Invarianten aufrecht zu erhalten, könnte man genauso gut entfernen Sie die member-Funktionen und die Daten der Mitglieder der öffentlichkeit.
  • Beachten Sie, dass die Deklaration der Funktionen inlinable hat wirklich keine bearance, ob Sie inline. In diesem Beispiel Point's Methoden sind implizit inline. Aber Sie sind auch nicht notwendig. Die Daten sollten öffentlich sein.
  • In Java jede (nicht-native) Funktion inlined, wenn der JITC entscheidet, es lohnt sich. In C++, wenn eine Funktion in einer anderen compilation unit, es kann nur eingebettet, wenn der linker ist sehr, sehr smart, oder wenn der compiler effektiv kompiliert alles.
  • Und eine Folge ist, dass in der JITC-optimierte Java getter-und setter-Funktionen sind "frei" -- null-Kosten vs Zugriff auf die Variablen direkt.
  • Diesen Reglern werden in der JVM für Java, nicht für den compiler. Die Hotspot-Optionen kann einen sehr großen Unterschied.
  • Sicher, aber OP ' s code, alles inline. Der performance-hit ist wahrscheinlich die Verwendung von std::list. Für die Getter würden diese optimiert werden, auch wenn inlined, aber mit Ihnen könnte resultieren in zusätzlichen Kopien der Daten der Mitglieder aus, weil Sie semantisch Werte zurückgeben. Jedenfalls ist es schwer zu sehen, wie Java konnte-ausführen von C++ in solchen einfachen numerischen problem, wenn richtig codiert, die in beiden Sprachen. Ich würde nicht erwarten, dass C++ zu Massiv aus-ausführen Java entweder.
  • Ich habe nicht bedeuten, andere Sprachen haben nicht diese Kontrollen, ich meinte, wie Sie näher an der Maschine Sprache, die Sie haben, mehr von diesen kontrolliert. Aber Sie haben absolut Recht.
  • Und solche smart-linkers vorhanden. Jeder gute compiler wird heute Optionen für inter-Modul-Optimierung. Die großen Unterschiede kommen von anderen Dingen: für einige Arten von Anwendungen, wie zum Beispiel die garbage collection wird ein großer Sieg, für die anderen mit zuteilen, alles dynamisch wird Sie töten. (Ich würde erwarten, dass seine zu den letzteren; er hat keine dynamische Zuordnungen, was so überhaupt, außerhalb seiner Liste. Aber die Tatsache, dass jeder push_back und pop_back zuordnen muss/frei ist, und das fehlen von Lokalität sind Mörder.)
  • Wenn richtig codiert und kompiliert mit Optimierung. Mit Java, brauchen Sie sich keine sorgen über die optimziation; JIT entscheidet, Wann und wo. Mit C++, müssen Sie dem compiler zu optimieren. (Und während ich würde nicht erwarten, dass ein massiver Unterschied, die Tatsache, dass er sich dynamisch zuweisen und jeden Point in Java könnte einen signifikanten Unterschied machen. Ich weiß nicht, ob die neuesten JIT-würde in der Lage sein, die Beseitigung, die.)
  • Plus die JIT-tatsächlich zu laufen. Und der garbage collector auch. BTW, ich vergaß, dass die benutzerdefinierten Typen zugeordnet werden musste dynamisch in java. Ich dachte, der array-basierte Probleme. Ich würde auch erwarten, dass diese einen Effekt haben.
  • Läuft der JIT kann ein Problem sein, besonders für ein kleines Programm wie seine. (Versuchen Sie, seinem Beispiel zu eine Millionen mal, und Sie können Sie ignorieren, die JIT.)
  • Und je nach code, der garbage collector kann schneller sein als die manuelle Zuordnung und die erforderlichen befreit. In seinem code, ist es fast sicher, dass die Gesamtkosten einer Zuweisung in Java billiger sein wird, als in C++, da er nie verwendet, genügend Speicherplatz für das auslösen der Sammler. Und selbst wenn Sie sich wiederholen, eine million mal, dies wäre wahr, weil Sie im optimalen Fall für die GC. Aber selbst der Schnellste Zuteilung ist langsamer als keine dynamische Allokation.
  • Wenn ich sehe, wie push_back ich erschaudern. Es gibt so viele Dinge, die jedes Programm langsam, andere als die Wahl der Sprache. Einige Menschen, die "grün um die Kiemen" zu denken, dass die Optimierung ist alles über compiler-Optimierung, wenn in der Tat, das sollte die Letzte Sache, die Sie betrachten.
  • Etwas zu merken, ist, dass Ihre Wahl von einem compiler hat einen realen Einfluss auf die Leistung. In einigen benchmarks, C-code, kompiliert mit gcc nicht laufen alle schneller als Java kompiliert mit einem guten compiler. Finden Sie diese stefankrause.net/wp/?p=6 und das stefankrause.net/wp/?p=6 für mehr info
  • C++ - code verwenden können, eine benutzerdefinierte Zuweisung (die aggregierten Zuweisungen und tun, nur ein einzelnes, frei später.) Das ist sogar noch schneller. Oder lassen Sie das frei heraus ein Vertrauen auf OS cleanup verlassen.
  • Vielleicht. Das ist mehr oder weniger wie die garbage collection funktioniert. Als einfach verlassen, das frei heraus; das ist genau das, was passiert, wenn Sie nicht genug Speicher, um garbage collection auszulösen (mit dem zusätzlichen plus, dass die Zuteilung algorithmen rechnen, die garbage collection sind schneller in der Verteilung). Natürlich in C++, können Sie verwenden einen garbage collector auch für die Fälle, in denen es die bessere Lösung ist.
  • Eigentlich, mit diesem Teil habe ich gemeint: Verwenden Sie eine Zuweisung explizit nicht unterstützt Freigabe an alle: Schneller und effizienter als die normale manuelle Freigabe sowie alle Müll-Sammler, selbst wenn es nie löst. (Das ist eine Optimierung für eine wirklich kleine Nische mit anständigen Dividenden...)
  • Ich sage Ihnen etwas - wenn Sie nicht planen, machen advenced Methoden (zum Beispiel in der künstlichen Intelligenz), der sollte ultra "optimiert", und jede Millisekunde wichtig ist... oder erweiterte Berechnungen in Grafik-oder ... etc.. Es ist nicht nötig, sich zu kümmern "C++ schneller als Java" - ja ist es, aber heute haben wir mächtig genug computer, um Sie nicht kümmern über eine solche Leistung in jedem normalen programm (natürlich sage ich nicht, dass über tradicional Optimalisierung wie makind nicht benötigt-loops 😛 ).. Java ist mächtig, denn Sie können die Verwendung von java fast überall mit hudge Bibliotheken unterstützen 😉
  • Alle allocators, die ich gesehen habe, die keine Unterstützung für die Freigabe auf alle arbeiten sehr viel, wie die allocators ein kopieren garbage collector; die Zuteilung der Zeit ist sehr, sehr schnell. Wo die garbage collection Kosten mehr in solchen Fällen ist, wenn es ausgelöst wird, und wenn Sie reservieren, wenig genug, so dass Sie nicht brauchen, um freigeben, dann die garbage collection sollte nicht immer auslösen, und Sie enden mit der Ausführung im wesentlichen den gleichen code.
  • Es hängt davon ab. Java ist sehr schlecht für numerische Anwendungen, wo Sie eine Menge von sehr kleinen Arten mit Wert-Semantik. Der JIT-nicht immer finden alle die Fälle, wo Sie konnten", so dass Sie am Ende mit zusätzlichen Zuweisungen, etc. die Kosten signifikante Echtzeit. Natürlich, der wahre Grund für Java nie benutzt wird, in solchen Fällen ist, dass die Sprache nicht ausdrucksstark genug, um lesbaren code schreiben für Sie. Am Ende, Java ist nicht für große scale-Programmierung, und es ist sehr schwierig zu schreiben, lesbar, zuverlässig-code in Java.
  • Aber der GC muss Kenntnis von der Art und Größe des jede Zuordnung. Die C++ - Lösung braucht es nicht, überhaupt, so die Verringerung der Zeit, die für die Zuteilung und die Größe der Zuweisungen.
  • sicherlich tut es wissen müssen, oder zumindest die Größe, als wenn Sie verstreut Blöcke von freigegebenen Speicher unter Speicher zugewiesen ist, an einem gewissen Punkt (wenn kein zusammenhängender block groß genug für seine nächste Zuteilung) hat es zu unterbrechen (wie ein gc, obwohl aus einem anderen Grund) und kompakte Speicher?
  • dies zeigt, man braucht eine Lizenz zur Verwendung von C++ (wie Sie eine Lizenz für eine Pistole)
  • Sry, aber du bist fehlt den Punkt: Wenn es keine Freigabe, es gibt keine Fragmentierung, keine Notwendigkeit für die Verfolgung von Arten, Größen und dergleichen.
  • Es gibt keine Programmier-Lizenz noch. Wie auch immer, wer wäre kompetent, Willens und vertrauenswürdig zu bewerten die Kompetenz von PHP, Java, Perl, C#, C++, [insert Programmiersprache] Programmierer?
  • was ich meine ist, dass, um frei, dass der Speicher irgendwann (es sei denn, Sie verwenden ein Betriebssystem, das nicht verwalten, Speicher) würde ich mir vorstellen, so etwas ist automatisch mit der Aufzeichnung der Größe der Zuweisung, und wie es sich durchsetzt unter Zuordnungen für andere Anwendungen (von denen jeder machen kann, - Freigabe und Fragmentierung etc.).
  • Aber wenn Sie nicht wnat, um den Speicher frei bisher, so dass es zu dem OS zu bereinigen, die auf Prozess beenden (was es ohnehin tun), es gibt keine Notwendigkeit zu halten Spur von jeder der Informationen, die notwendig ist, um freie zugewiesenen Blöcke, damit Sie nur nicht.
  • ja, macht Sinn. Ich dachte, ich würde es nicht, wenn Sie befreit es, aber wenn es tut, so oder so, warum dann nicht.
  • Ich würde wirklich empfehlen Sie nicht die Verwendung andere Sprache als Englisch für Variablen und Funktion von Namen. Manchmal ist Ihr code wird gelesen von Menschen, die nicht Französisch verstehen (wie, glaube ich, hier bei stack-overflow), das macht es schwerer für Sie, Lesen Sie Ihren code.

InformationsquelleAutor realUser404 | 2014-07-23
Schreibe einen Kommentar