Programm endet viel zu spät



  • Hallo,

    ich habe ein Programm geschrieben, das so weit funktioniert wie es soll. Allerdings endet es nicht bzw. nur sehr spät. Und zwar taucht am Ende eines Programms im Konsolenfenster normalerweise immer "Drücken Sie eine beliebige Taste ..." auf. Aber in meinem Programm taucht es erst nach einigen Minuten auf. Selbst wenn der eigentliche Ablauf schon längst fertig ist. Nun weiß ich nicht woran es liegt. Der Computer rechnet noch irgendwas. Nur was? Die Zeit, die ich bis zum "Drücken Sie eine beliebige Taste ... " warte, dauert auch länger, je mehr Objekte ich verarbeite (ich speicher mehrere Objekte in einer std::unordered_map ab). Mache ich vergleichsweise wenig, endet das Programm sofort. Mache ich 100 mal mehr, muss ich endlos lange warten, auch wenn die letzte Zeile von meiner test_methode() schon längst erreicht ist. Was auch auffällt ist, dass ich dann nicht sofort zur aufrufenden main Methode springe, sondern erst nach dem langen Warten...

    Kann mir jemand sagen woran das liegt oder liegen könnte?



  • ichbineinnoob schrieb:

    Kann mir jemand sagen woran das liegt oder liegen könnte?

    Ein Debug-Heap und ein Speicherloch.



  • volkard schrieb:

    ichbineinnoob schrieb:

    Kann mir jemand sagen woran das liegt oder liegen könnte?

    Ein Debug-Heap und ein Speicherloch.

    Kannst du mir das erklären? Ich verstehe leider nicht worauf du hinaus willst.

    Ich habe ein Programm geschrieben, wie ich es auch in Java getan hätte. Wobei ich nirgends manuell Speicher angefordert oder freigegeben habe. Darum verstehe ich nicht wie es dazu kommen kann. 😕



  • Wenn Du selber nicht

    new
    

    geschrieben hast, wird es was anderes sein.
    Vielleicht eine verkettete Liste, die mit Dutzenden von Millionen Objekten befüllt wurde und bei Programmende zerstört wird. Kann vielleicht auch passieren, wenn die Hash-Funktion kaputt ist und viele Objekte den gleichen Hash-Wert annehmen. Oder wenn viele Objekte hinzugefügt werden, obwohl sie schon drin sein. Das würde auch erklären, daß trozdem keine langen Suchzeiten entstehen.



  • volkard schrieb:

    Wenn Du selber nicht

    new
    

    geschrieben hast, wird es was anderes sein.
    Vielleicht eine verkettete Liste, die mit Dutzenden von Millionen Objekten befüllt wurde und bei Programmende zerstört wird. Kann vielleicht auch passieren, wenn die Hash-Funktion kaputt ist und viele Objekte den gleichen Hash-Wert annehmen. Oder wenn viele Objekte hinzugefügt werden, obwohl sie schon drin sein. Das würde auch erklären, daß trozdem keine langen Suchzeiten entstehen.

    Ich habe absichtlich kein new benutzt, da ich zunächst nur eine std::unordered_map füllen und keine unbeabsichtigten Fehler produzieren wollte.

    Meine Hashfunktion ist wohl ziemlich schlecht (ich möchte (vorerst) 2-dimensionale Koordinaten abspeichern und multipliziere diese einfach mit 2 verschieden großen Primzahlen). Trotzdem werden meine Objekte relativ gut in der Hashtabelle verteilt (in den meisten Buckets sind nur höchstens 4 Objekte, bei recht großem Füllgrad).

    Verkettete Listen benutze ich nicht. Oder meinst du, diese werden in den Buckets benutzt? Aber dort befinden sich ja auch nur höchstens 4 Elemente.

    Dutzende von Millionen Einträge wären auch schön. Das Programm scheitert bereits bei einer Viertelmillionen... 😞



  • ichbineinnoob schrieb:

    Meine Hashfunktion ist wohl ziemlich schlecht (ich möchte (vorerst) 2-dimensionale Koordinaten abspeichern und multipliziere diese einfach mit 2 verschieden großen Primzahlen).

    Die ist sehr gut, würde ich meinen (außer zufällig ist p*k==1 mit kleinem k oder so).

    Also versagt hier meine Kristallkugel. Ich habe keine weitere Vermutung.



  • volkard schrieb:

    ichbineinnoob schrieb:

    Meine Hashfunktion ist wohl ziemlich schlecht (ich möchte (vorerst) 2-dimensionale Koordinaten abspeichern und multipliziere diese einfach mit 2 verschieden großen Primzahlen).

    Die ist sehr gut, würde ich meinen (außer zufällig ist p*k==1 mit kleinem k oder so).

    Also versagt hier meine Kristallkugel. Ich habe keine weitere Vermutung.

    Ich danke dir trotzdem vielmals.

    Ich glaube ich poste gleich mal ein bisschen Code.



  • ichbineinnoob schrieb:

    Verkettete Listen benutze ich nicht. Oder meinst du, diese werden in den Buckets benutzt? Aber dort befinden sich ja auch nur höchstens 4 Elemente.

    Dutzende von Millionen Einträge wären auch schön. Das Programm scheitert bereits bei einer Viertelmillionen... 😞

    Ähm. Es läuft eine Stunde lang und macht tolle Sachen und sammelt dabei eine viertel Million Objekte? Das Löschen dieser viertel Million Objekte könnte viel Zeit brauchen. Ja, ich meine die verketteten Listen von der Hashtable.
    Mal abschätzen...
    Pro Objekt werden mindestens 32 Bytes Speicher benutzt. Macht 8MB. Das ist ja gar nichts.
    Mal ein delete mit 100 Takten ansetzen. Macht 25000000 Takte, das ist nur ein viertel Sekündchen.
    Es sollte keinen Ärger geben.


  • Mod

    Nur um ganz sicher zu sein: Der Compiler ist schon irgendwie ein bisschen auf optimieren eingestellt?

    Nicht dass es am Ende alles daran liegt, dass du eine furchtbar lahme Debugversion eines Containers benutzt.



  • SeppJ schrieb:

    Nur um ganz sicher zu sein: Der Compiler ist schon irgendwie ein bisschen auf optimieren eingestellt?

    Nicht dass es am Ende alles daran liegt, dass du eine furchtbar lahme Debugversion eines Containers benutzt.

    Muss man den irgendwie einstellen? Wie macht man das?

    Ich habe Visual C++ 2008 Express installiert und nichts eingestellt.



  • Ich poste einfach mal den Code. Wäre toll, wenn sich jemand die Zeit nehmen würde um ihn zu lesen. Er ist auch nicht schwer zu verstehen.

    Das ist der Graph, in dem die unordered_map ist:

    typedef unordered_map<pair<Zustand,Aktion>,Zustand,meinHash> my_map;
    
    class Graph
    {
    public:
    	Graph() { kanten.max_load_factor(1.1f); };
    
    	void fuegeKanteHinzu(const Zustand&, const Aktion&, const Zustand&) throw (...);
    	void fuegeKanteHinzu_insecure(const Zustand&, const Aktion&, const Zustand&);
    	Zustand& get(const Zustand&, const Aktion&) const throw (...);
    
    //private:	
    	my_map kanten;
    };
    
    void Graph::fuegeKanteHinzu_insecure(const Zustand &von, const Aktion &mit, const Zustand &nach)
    {
    	//pair<Zustand,Aktion> p(von,mit);
    	kanten.insert(my_map::value_type(make_pair(von,mit),nach));
    };
    

    So sieht ein Zustand aus:

    class Zustand
    {
    public:
    	Zustand(const int& x, const int& y) : k(x,y) { };
    	Zustand(const Koordinate& k) : k(k) { };
    	Zustand() : k(0,0) { }; 
    	~Zustand() {};
    
    	inline int getHash() const;
    
    	friend bool operator<(const Zustand&, const Zustand&);
    	friend bool operator==(const Zustand&, const Zustand&);
        friend ostream& operator<<(ostream&, const Zustand&);
    	friend Zustand& operator+(const Zustand&, const Aktion&);
    	friend Zustand& operator+(const Zustand&, const Zustand&);
    
    //private:
    	Koordinate k;
    };
    
    inline int Zustand::getHash() const
    {
    	return k.getX()*5827 + k.getY()*1091;
    };
    

    Und das ist die main-Methode:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	cout << "BEGIN" << endl;
    	graph_zeit_test();
    	cout << "END" << endl;
    	return 0;
    };
    
    void graph_zeit_test()
    {
    	__int64 ctr1 = 0, ctr2 = 0;
    	Graph g;
    
    	Aktion a(0,1), b(1,0), c(-1,0), d(0,-1);
    
    	Zustand zz(-1,-1);
    	QueryPerformanceCounter((LARGE_INTEGER *)&ctr1);
    	for (int x=0; x<250; x++)
    	{
    		for (int y=0; y<250; y++)
    		{
    			Zustand z(x,y);
    			g.fuegeKanteHinzu(z,a,zz);
    			g.fuegeKanteHinzu(z,b,zz);
    			g.fuegeKanteHinzu(z,c,zz);
    			g.fuegeKanteHinzu(z,d,zz);
    		}
    	}
    	QueryPerformanceCounter((LARGE_INTEGER *)&ctr2);
    	cout << "Initialisierung:  " << (ctr2-ctr1) << endl;
    }
    

    Die Methode graph_zeit_test() wird bis zur letzten Ausgabe ausgeführt.

    Aber die letzte Ausgabe "END" in der main-Methode wird erst nach vielen Minuten ausgeführt..



  • Ich denke, Du meintest

    kanten.max_load_factor(0.8f);
    

    Der Code ist unverdächtig.



  • Sichern wir mal ab, daß im Destruktor vom Graphen die Zeit gefressen wird:

    void graph_zeit_test()
    {
    	__int64 ctr1 = 0, ctr2 = 0;
    {//neuer Block für g
    	Graph g;
    
    	Aktion a(0,1), b(1,0), c(-1,0), d(0,-1);
    
    	Zustand zz(-1,-1);
    	QueryPerformanceCounter((LARGE_INTEGER *)&ctr1);
    	for (int x=0; x<250; x++)
    	{
    		for (int y=0; y<250; y++)
    		{
    			Zustand z(x,y);
    			g.fuegeKanteHinzu(z,a,zz);
    			g.fuegeKanteHinzu(z,b,zz);
    			g.fuegeKanteHinzu(z,c,zz);
    			g.fuegeKanteHinzu(z,d,zz);
    		}
    	}
    cout<<"test1\n"<<flush;
    }//Ende neuer Block für g, g wird gelöscht. 
    cout<<"test2\n"<<flush;
    	QueryPerformanceCounter((LARGE_INTEGER *)&ctr2);
    	cout << "Initialisierung:  " << (ctr2-ctr1) << endl;
    }
    

    So wird die Zeit zwischen test1 und test2 gefressen?


  • Mod

    volkard schrieb:

    Der Code ist unverdächtig.

    Eventuell ist ja in den fehlenden Teilen noch etwas versteckt. @Threadersteller: Kannst du noch die Definitionen von Aktion und Graph::fuegeKanteHinzu geben, so dass man das Beispiel selber compilieren kann?



  • volkard schrieb:

    Ich denke, Du meintest

    kanten.max_load_factor(0.8f);
    

    Der Code ist unverdächtig.

    Hm? Wieso sollte ich 0.8f gemeint haben? Vorgegeben ist, so weit ich weiß, 4.0f, was mir viel zu viel schien. Darum habe ich 1.1f genommen.

    Meinst du den letzten Satz ironisch?



  • ichbineinnoob schrieb:

    Meinst du den letzten Satz ironisch?

    Nein, ich meinte mich daran zu erinnern, daß 0.8 gut wäre. Hängt aber viel zu stark von der Implementierung ab, als daß ich sagen könnte, 0.8 wäre bei Dir auch gut.



  • Häng dich doch einfach mit dem Debugger ran und drück auf Pause, wenn es lang braucht, dann bist du genau da im Code.



  • SeppJ schrieb:

    volkard schrieb:

    Der Code ist unverdächtig.

    Eventuell ist ja in den fehlenden Teilen noch etwas versteckt. @Threadersteller: Kannst du noch die Definitionen von Aktion und Graph::fuegeKanteHinzu geben, so dass man das Beispiel selber compilieren kann?

    Das bringt leider nichts, weil ich noch die Operatoren etc. posten müsste.
    Soll ich das einfach mal machen?

    Eine Aktion sind so aus:

    class Aktion
    {
    public:
    	Aktion(const int& x, const int& y) : k(x,y) { };
    	Aktion(const Koordinate& k) : k(k) { };
    	Aktion() : k(0,0) {};
    	~Aktion() {};
    
    	inline int getHash() const;
    
    	friend bool operator<(const Aktion&, const Aktion&);
    	friend bool operator==(const Aktion&, const Aktion&);
    
    private:
    	Koordinate k;
    };
    
    inline int Aktion::getHash() const
    {
    	return k.getX()*7159 + k.getY()*929;
    };
    

    Die eine Methode so:

    void Graph::fuegeKanteHinzu(const Zustand &von, const Aktion &mit, const Zustand &nach)
    {
    	pair<Zustand,Aktion> p(von,mit);
    	my_map::const_iterator iter = kanten.find(p);
    	if (iter != kanten.end())
    	{
    		if ((*iter).second == nach) throw KanteBereitsVorhandenException(von, mit, nach);
    		else throw KantenKonfliktException(von, mit, (*iter).second, nach);
    	}
    	kanten.insert(my_map::value_type(p,nach));
    };
    

    Obwohl sich da, außer ein paar Milisekunden, nichts tut.



  • ichbineinnoob schrieb:

    Muss man den irgendwie einstellen? Wie macht man das?

    Ich habe Visual C++ 2008 Express installiert und nichts eingestellt.

    Starte im Release-Modus, und starte ohne Debugging (das "hohle" grüne Dreieck).


Log in to reply