Zeit in Millisekunden oder noch kleiner



  • Hallo,

    ich habe ein Programm geschrieben, das meiner Meinung nach viel zu langsam ist. Bereits bei 10.000 Objekten funktioniert es schon sehr langsam (ich iteriere durch mehrere Listen).
    Darum wollte ich wissen welche Methode so viel Zeit beansprucht.

    Nur... wie mache ich das?

    Ich habe einige Beispiele im Internet gesehen, für die ich aber <sys/time.h> inkluden muss. Leider habe ich Visual C++ 2008 Express, das diese Bibliothek nicht kennt.

    Gibt es sonst noch eine Möglichkeit?

    Ich brauche weder eine Sekundenanzeige oder sonstigen Schnickschnack. Ein Timestamp im Millisekundenbereich (oder kleiner) würde ja schon reichen.

    Danke für jede Hilfe!



  • Ein Ansatz wäre clock() aus der <ctime> (wenn der Compiler das nicht kennt, ist er nicht standardkonform). Unter Windows könntest auch noch mit QueryPerformanceCounter() arbeiten, wenn dir das zu ungenau ist.


  • Mod

    Genaue Zeitmessung in Methoden macht man nicht von Hand, man benutzt dafür einen Profiler.

    Aber wenn du bereits bei 10.000 Objekten Schwierigkeiten bekommst vermute ich kein Problem mangelnder Feinoptimierung, sondern massive Designschwächen. Vielleicht stellst du dein Programm mal kurz(!) vor?



  • CStoll schrieb:

    Ein Ansatz wäre clock() aus der <ctime> (wenn der Compiler das nicht kennt, ist er nicht standardkonform). Unter Windows könntest auch noch mit QueryPerformanceCounter() arbeiten, wenn dir das zu ungenau ist.

    Dank dir!

    Ich habe zunächst clock() ausprobiert. Das scheint wohl nicht genau genug zu sein. Aber was zählt die Methode eigentlich? Bei Microsoft steht was von clock ticks. Was isn das?



  • Clock ticks sind eine im ANSI Standard definierte Zeiteinheit, deren genauer Wert vom System abhängt - um das in Sekunden umzurechnen, gibt es die Konstante CLOCKS_PER_SEC.



  • SeppJ schrieb:

    Genaue Zeitmessung in Methoden macht man nicht von Hand, man benutzt dafür einen Profiler.

    Aber wenn du bereits bei 10.000 Objekten Schwierigkeiten bekommst vermute ich kein Problem mangelnder Feinoptimierung, sondern massive Designschwächen. Vielleicht stellst du dein Programm mal kurz(!) vor?

    Designschwäche? Man dieses Wort hasse ich. Ich schreibe schon seit Wochen an diesem Programm (ich brauche es fürs Studium - bin aber kein C++ Freak - mit Java wär das alles kein Thema) und das was ich bislang habe, werde ich vielleicht verwerfen müssen. -.-

    Also, ich fange mal an. Ich versuche derzeit nur ein Graph aufzubauen. Da ich mehrere brauche, habe ich einfach eine Klasse Graph erstellt.

    Darin enthalten ist eine Liste aus Zuständen

    list<pair<Zustand,Zustand*>> zustaende;
    

    und aus Aktionen.

    Die Zustände bilden die Knoten und die Aktionen die Kanten.

    Aus einem Zustand gelangt man mit einer Aktion in einen anderen Zustand!
    Das habe ich so versucht nachzubilden:

    map<pair<Zustand*,Aktion*>,Zustand*> kanten;
    

    Die Zeiger habe ich genommen um Speicherplatz zu sparen.

    Dazu kommen halt diverse Methoden wie

    Zustand* findeZustand(Zustand*)
    

    mit denen ich durch die Liste iteriere

    for (list<pair<Zustand,Zustand*>>::iterator i_z = zustaende.begin(); i_z != zustaende.end(); i_z++)
    

    Ach ja, vielleicht sehen die Zeiger unvorteilhaft aus. Aber ich dachte halt so kann ich Speicherplatz speichern. Sonst wäre der Speicheraufwand polynomial.



  • Wenn du so oft in den Listen suchen willst, solltest du eine Datenstruktur verwenden, in der das schneller geht, z.B. ein std::set<>.

    PS: Außerdem solltest du für die Arbeit mit den Zeigern eigene Vergleichs-Funktionen bereitstellen, der Vergleich zwischen unabhängigen Zeigern (d.h. alles was nicht ins selbe Array reinzeigt) ist nämlich undefiniert.



  • CStoll schrieb:

    Wenn du so oft in den Listen suchen willst, solltest du eine Datenstruktur verwenden, in der das schneller geht, z.B. ein std::set<>.

    PS: Außerdem solltest du für die Arbeit mit den Zeigern eigene Vergleichs-Funktionen bereitstellen, der Vergleich zwischen unabhängigen Zeigern (d.h. alles was nicht ins selbe Array reinzeigt) ist nämlich undefiniert.

    Was ist ein std::set<> ? Ist das so etwas wie eine Hashtabelle? Ich dachte std::map<> wäre das bereits.

    Die Zeiger habe ich gewählt um einfach nur die Adressen zu vergleichen. Zeigen 2 Zustands-Pointer auf die selbe Adresse, so sind diese gleich. Das geht doch schneller, oder?

    Zustand a(1,2), b(1,2);
    Zustand *z1 = findeZustand(a), *z2 = findeZustand(b); //  bekommen die selbe Adresse
    if (z1==z2) cout << "Zustand gleich!" << endl;  // so meinte ich das
    


  • set<> ist ein recht naher Verwandter von map<>, das keine Schlüssel-Wert-Paare verwaltet, sondern nur Schlüssel - da es genau wie die map als Binärbaum aufgebaut ist, bekommst du die Suche auch in logarithmischer Zeit hin.

    Die Zeiger habe ich gewählt um einfach nur die Adressen zu vergleichen. Zeigen 2 Zustands-Pointer auf die selbe Adresse, so sind diese gleich. Das geht doch schneller, oder?

    Gleichheit ist ja auch kein Problem. Aber die map<> verwendet eine größer-als-Relation, um die Elemente in eine Reihenfolge zu bringen und die ist laut Standard nicht garantiert bei unabhängig voneinander existierenden Zeigern.



  • ichbineinnoob schrieb:

    Die Zeiger habe ich gewählt um einfach nur die Adressen zu vergleichen. Zeigen 2 Zustands-Pointer auf die selbe Adresse, so sind diese gleich. Das geht doch schneller, oder?

    Zustand a(1,2), b(1,2);
    Zustand *z1 = findeZustand(a), *z2 = findeZustand(b); //  bekommen die selbe Adresse
    if (z1==z2) cout << "Zustand gleich!" << endl;  // so meinte ich das
    

    Was bedeutet (1,2) beim Erzeugen eines Zustands?


  • Mod

    ichbineinnoob schrieb:

    Die Zeiger habe ich gewählt um einfach nur die Adressen zu vergleichen. Zeigen 2 Zustands-Pointer auf die selbe Adresse, so sind diese gleich. Das geht doch schneller, oder?

    Sei dir da mal nicht so sicher. Du baust dir eine Indirektion ein, um was zu sparen? Den Vergleich zweier Zahlen? Und umständlicher wird der Code dadurch auch noch.

    Die Zeiger habe ich genommen um Speicherplatz zu sparen.

    😕

    Ich glaube dein Programm ist, das muss man einfach mal so sagen, ein Paradebeispiel für den Spruch "Premature optimization is the root of all evil". Anstatt dich gründlich mit den grundlegenden Datenstrukturen zu befassen und damit ein schönes, schnelles, leicht zu schreibendes und (voraussichtlich) auch schnelles Programm zu schreiben, hast du alles unnötig umständlich gemacht. Und die zugrunde liegenden Überlegungen sind noch nicht einmal richtig, was die Sache noch schlimmer macht.



  • Nochmal kurz die Zwischenfrage zum Vergleich von Adressen.

    vector<Foo*> foos(/*...*/);
    sort(foos.begin(), foos.end());
    Foo* kandidat = /*...*/;
    bool kandidatIstDabei = 
        binary_search(foos.begin(), foos.end(), kandidat);
    

    Ist das tatsächlich undefiniert oder in irgendeiner Weise problematisch? Ich mach sowas nämlich auch gelegentlich. Ich möchte dabei auch keine Reihenfolge definieren, sondern die Adresse nur als Identität des Objekts verwenden.


  • Mod

    brotbernd schrieb:

    Ist das tatsächlich undefiniert oder in irgendeiner Weise problematisch? Ich mach sowas nämlich auch gelegentlich. Ich möchte dabei auch keine Reihenfolge definieren, sondern die Adresse nur als Identität des Objekts verwenden.

    Wenn das der Vergleich ist, den du wünscht, dann ist das auch richtig. Die meisten Leute verstehen aber unter Gleichheit eben etwas anderes.



  • Mich hatte jetzt das hier etwas irritiert

    CStoll schrieb:

    PS: Außerdem solltest du für die Arbeit mit den Zeigern eigene Vergleichs-Funktionen bereitstellen, der Vergleich zwischen unabhängigen Zeigern (d.h. alles was nicht ins selbe Array reinzeigt) ist nämlich undefiniert.


  • Mod

    brotbernd schrieb:

    Mich hatte jetzt das hier etwas irritiert

    CStoll schrieb:

    PS: Außerdem solltest du für die Arbeit mit den Zeigern eigene Vergleichs-Funktionen bereitstellen, der Vergleich zwischen unabhängigen Zeigern (d.h. alles was nicht ins selbe Array reinzeigt) ist nämlich undefiniert.

    Ahh, stimmt, da war ja was 😃 . Ja, das kann auf manchen Maschinen in die Hose gehen. Aber das ist so exotisch, dass ich da nicht mehr dran denke.
    Die Standardbibliotheken machen solche Geschichten intern übrigens auch, das ist es moralisch gerechtfertigt das selber auch so zu machen. Ich sage mal, das ist so ein Fall, wo man die Portabilität wirklich zu weit treibt, wenn man auf so etwas noch achtet. Das ist wie anzunehmen, dass der Zeichensatz nicht ASCII kompatibel wäre oder das Byte nicht 8 Bit hätte. Klar, es ist nicht definiert, aber man muss schon ganz schön suchen für ein reales Gegenbeispiel.

    Vielleicht noch interessant dazu ist diese uralte Diskussion:
    http://groups.google.com/group/comp.std.c++/browse_thread/thread/604972347050fbd6/2c5509bf63bb3e7d?lnk=gst&q=pointer+less+set#2c5509bf63bb3e7d



  • SeppJ schrieb:

    Ich glaube dein Programm ist, das muss man einfach mal so sagen, ein Paradebeispiel für den Spruch "Premature optimization is the root of all evil". Anstatt dich gründlich mit den grundlegenden Datenstrukturen zu befassen und damit ein schönes, schnelles, leicht zu schreibendes und (voraussichtlich) auch schnelles Programm zu schreiben, hast du alles unnötig umständlich gemacht. Und die zugrunde liegenden Überlegungen sind noch nicht einmal richtig, was die Sache noch schlimmer macht.

    Ich stehe unter Zeitdruck und habe leider keine Zeit mich gründlich mit den grundlegenden Datenstrukturen zu befassen (so gern ich das täte!). Ich muss in vorgegebener Zeit ein Lernalgorithmus programmieren. Welche Datenstrukturen ich benutze ist zweitrangig (aber scheinbar auch nicht unwichtig)...
    Es mag sein, dass ich einiges versaut habe, aber die zugrunde liegenden Überlegungen sind nicht unbedingt falsch. Nur scheine ich einige Aspekte nicht bedacht zu haben. Ich wollte halt nicht nur auf die Geschwindigkeit achten, sondern auch auf den Speicherverbrauch (weswegen ich dachte Pointer wären sinnvoll) und dazu sollten die Daten auch persistent sein (was diese komische Speicherstruktur erklärt.

    Nun ja, ich bin für jede Hilfe mordsmäßig dankbar! Also sag mir doch bitte nicht nur, dass ich falsch gedacht habe, sondern auch inwiefern. Danke!



  • Um noch einmal mehr oder weniger auf das Thema zurück zu kommen.

    Die Suche in der std::list ist schon bei nur 1.500 Objekten 240 mal langsamer als eine Einfügeaktion in der std::map. So krass ist hätte ich den Aufwand nicht erwartet.

    Wenn ich die std::list in eine std::set ändere, dann funktioniert das Suchen sicher schneller. Aber ich müsste (nach meinen Überlegungen) auch die Pointer weg lassen, weil sich Hashtabellen ständig ändern. Ich müsste anstatt 3n Objekte + Pointer, n² abspeichern. Ich hoffe das ist nicht so schlimm.



  • set/map verwenden keine Hash-Tabellen, sondern einen binären Suchbaum. Und sie garantieren beide, daß Iteratoren und Zeiger auf ihre Elemente gültig bleiben, solange du nicht das betroffene Element selber löschst.

    Ein Problem könnte das set<> mit Default-Sortierung und deiner Such-Methode von dort oben ergeben - wenn du die Adressen vegleichst, wirst du das lokal angelegte Vergleichs-Objekt bestimmt nicht in der Sammlung finden.

    (wenn du mit Hash-Tabellen arbeiten willst, gibt es in C++0x die unordered_set<> und Kollegen)

    Ansonsten: Wie groß sind deine Zustände und Aktionen überhaupt? Wenn die nur aus zwei Zahlen bestehen, lohnt sich der Aufwand mit den Zeigern nicht wirklich.


Log in to reply