Schneller speichern und laden



  • Dann sollte es so funktionieren:

    // Achtung: Pseudo-Code, subchunk1 und subchunk2 müssen richtig definiert werden...
    for( ci )
    {
       Chunk **subchunk1 = this->chunks[ci];
       for( cj )
       {
          Chunk *subchunk2 = subchunk1[ci];
          for( ck )
          {
             chunk = subchunk2[ck];
          }
       }
    }
    


  • Nein, geht nicht.



  • Das zu optimieren ist ohnehin eher die Aufgabe des Compilers.



  • Die erinnerung schrieb:

    Nein, geht nicht.

    Funktioniert das:

    for( ci )
    {
       Chunk (*subchunk1)[16] = this->chunks[ci];
       for( cj )
       {
          Chunk *subchunk2 = subchunk1[ci];
          for( ck )
          {
             chunk = subchunk2[ck];
          }
       }
    }
    

    Natürlich müssen die for-Schleifen noch ausgeschrieben werden ...



  • headmyshoulder schrieb:

    this->chunks[ci][cj][ck] machts langsam.

    Wieso sollte das was langsam machen? Compiler können sowas schön optimieren.



  • hustbaer schrieb:

    headmyshoulder schrieb:

    this->chunks[ci][cj][ck] machts langsam.

    Wieso sollte das was langsam machen? Compiler können sowas schön optimieren.

    this->chunks[ci][cj][ck] sind drei Aufrufe des []-Operators, subchunk[ck] ist ein Aufruf. Im ersten Fall treten N1*N2*N3*3 dieser Aufrufe auf, im zweiten Fall N1+N1*N2+N1*N2*N3, was knapp ein drittel ist. Wenn der Compiler das Optimieren will muss er Logik in die Schleifen über cj und ci ziehen. Drauf würde ich mich nicht verlassen. Es kann sein, dass das hier klappt da das Arrays sind. Ist chunk aber ein vector< vector< vector< T > > > wird man dadurch ganz sicher Performance verlieren.



  • headmyshoulder schrieb:

    hustbaer schrieb:

    headmyshoulder schrieb:

    this->chunks[ci][cj][ck] machts langsam.

    Wieso sollte das was langsam machen? Compiler können sowas schön optimieren.

    this->chunks[ci][cj][ck] sind drei Aufrufe des []-Operators, subchunk[ck] ist ein Aufruf. Im ersten Fall treten N1*N2*N3*3 dieser Aufrufe auf, im zweiten Fall N1+N1*N2+N1*N2*N3, was knapp ein drittel ist. Wenn der Compiler das Optimieren will muss er Logik in die Schleifen über cj und ci ziehen.

    Ja, tut er auch.

    Drauf würde ich mich nicht verlassen.

    Ich schon, weil ich häufig gesehen habe dass es funktioniert. Und eigentlich noch keinen derart einfachen Fall wo es nicht funktioniert hätte.

    Es kann sein, dass das hier klappt da das Arrays sind. Ist chunk aber ein vector< vector< vector< T > > > wird man dadurch ganz sicher Performance verlieren.

    Selbst da wäre ich mir nicht sicher dass der Compiler es nicht optimiert.
    Compiler sind recht gut darin konstante Ausdrücke aus Schleifen rauszuziehen. Auch wenn diese erst durch inlining diverser Funktionen entstehen.

    Natürlich schadet es nichts, ich würde aber trotzdem erstmal gucken ob es was bringt.
    Jede Optimierung macht den Code schwerer nachzuvollziehen/schwerer auf Fehler zu untersuchen etc. Jede unnötige Optimierung ist daher mMn. als schlechter Code zu werten.

    Wobei ich in dem (hypothetischen) Fall mit verschachtelten Vektoren es noch als OK ansehen würde hier die optimierte Schleife zu verwenden. Weil da viele Dinge mit reinspielen, von denen man oft nicht erwartet dass sie einen Unterschied machen können. D.h. eine ganz harmlos aussehende Änderung kann dazu führen dass der Compiler die Optimierung auf einmal nicht mehr packt.



  • hustbaer schrieb:

    Drauf würde ich mich nicht verlassen.

    Ich schon, weil ich häufig gesehen habe dass es funktioniert. Und eigentlich noch keinen derart einfachen Fall wo es nicht funktioniert hätte.

    Es kann sein, dass das hier klappt da das Arrays sind. Ist chunk aber ein vector< vector< vector< T > > > wird man dadurch ganz sicher Performance verlieren.

    Selbst da wäre ich mir nicht sicher dass der Compiler es nicht optimiert.
    Compiler sind recht gut darin konstante Ausdrücke aus Schleifen rauszuziehen. Auch wenn diese erst durch inlining diverser Funktionen entstehen.

    Natürlich schadet es nichts, ich würde aber trotzdem erstmal gucken ob es was bringt.
    Jede Optimierung macht den Code schwerer nachzuvollziehen/schwerer auf Fehler zu untersuchen etc. Jede unnötige Optimierung ist daher mMn. als schlechter Code zu werten.

    Wobei ich in dem (hypothetischen) Fall mit verschachtelten Vektoren es noch als OK ansehen würde hier die optimierte Schleife zu verwenden. Weil da viele Dinge mit reinspielen, von denen man oft nicht erwartet dass sie einen Unterschied machen können. D.h. eine ganz harmlos aussehende Änderung kann dazu führen dass der Compiler die Optimierung auf einmal nicht mehr packt.

    Ich hab durch genau diese Art von Optimierung die Laufzeit eines Programmes schon um mehrere Faktoren verbessert. Ist aber schon ein Weile her (ca. 7 Jahre) und wahrscheinlich können moderne Compiler jetzt viel besser optimieren. Ich vermute auch stark dass alle linearen Algebra-Packages sowas machen. Ok, man muss da auch nicht lange rumrätseln. Ich werd einen Testfall schreiben und das Ergebniss hier posten. Mal sehn was rauskommt.



  • Eigentlich sind 4 Sekunden vollkommen ok und ich glaube nicht, dass ich damit nochmals Sekunden raushole und dann lohnt es sich nicht.

    Ich vertraue mal meinem Compiler...



  • headmyshoulder schrieb:

    Ich werd einen Testfall schreiben und das Ergebniss hier posten. Mal sehn was rauskommt.

    Wenn es dir die Sache wert ist, immer her damit.



  • Ich wollte mal anmerken, dass ofstream ein puffernder Stream ist. Es ist also fast egal, ob man da byteweise oder in grösseren Blöcken schreibt. Der ofstream übergibt die Daten in der Regel in 8k Blöcken an das Betriebssystem.

    Und einer hat hier Locking erwähnt. Die put-Methode ist nicht thread-safe und wird nicht locken. Auch das macht nichts aus.

    Dann hat noch einer erwähnt, dass std::vector mit Sicherheit langsamer wäre, als arrays. Auch das stimmt nicht. Der Compiler sieht die komplette Implementierung des std::vector und kann daher genau die selben Optimierungen durchführen, wie mit arrays. std::vector ist ja gewissermaßen "nur" syntactic sugar (wobei ich darauf nicht verzichten will).

    Aber das Resümee hier sollte sein, dass eine Performancemessung ohne Optimierung nichts bringt.

    Ach ja - die Aussagen bezüglich ofstream gelten nicht für den xlC-Compiler. Da sind die iostreams indiskutabel langsam. Aber ich bezweifle, dass hier jemand mit dem xlC arbeitet.



  • Das ostream bufferd habe ich gewusst, ich dachte deshalb ja anfangs auch, dass das mit dem Vekto sinnlos ist. Hat ja auch nur 2 Sekunden rausgeholt...



  • tntnet schrieb:

    std::vector ist ja gewissermaßen "nur" syntactic sugar

    Naja, es ist syntactic sugar für mit new[] angelegte Arrays, was hier allerdings nicht der Fall ist.
    Von der Natur her entspricht einem multidimensionalen Array wie Block blocks[8][8][8]; grob eher ein solches Konstrukt:

    vector<Block> blocks;
        Chunk() : blocks(8*8*8) {}
        Block& getBlock(int x,int y,int z) {return blocks[x*64+y*8+z];}
    

    Und in der Tat, auch mit vector bleibt die Laufzeit bei mir weitgehend unverändert (0.20s statt 0.18s).

    @Die erinnerung
    Selbst mit deinen Klassen läuft das bei mir noch in 0.45s. Irgendwie habe ich das Gefühl, dass da immer noch nicht alles optimal läuft (es sei denn, du hast noch einen Pentium II oder ähnliches).
    Aber wenn du schon zufrieden bist, dann ist ja gut.



  • Athar schrieb:

    tntnet schrieb:

    std::vector ist ja gewissermaßen "nur" syntactic sugar

    Naja, es ist syntactic sugar für mit new[] angelegte Arrays, was hier allerdings nicht der Fall ist.

    Nö, ist er nicht.



  • Möge er sich länger fassen.


  • Mod

    Athar schrieb:

    Möge er sich länger fassen.

    vector ist syntaktischer Zucker für std::allocator<T>, welcher syntaktischer Zucker für operator new(T) ist, welcher syntaktischer Zucker für new T ist.



  • Welcher syntaktischer Zucker für malloc() und placement new ist, welches syntaktischer Zucker für Konstruktoraufruf + Cast ist? 🤡



  • hustbaer schrieb:

    headmyshoulder schrieb:

    Ich werd einen Testfall schreiben und das Ergebniss hier posten. Mal sehn was rauskommt.

    Wenn es dir die Sache wert ist, immer her damit.

    Ok, ich geb mich geschlagen. In meinen Tests waren beide Varianten gleich schnell.

    Ich hab drei Tests durchgeführt:

    • array< array< array< double , N3 > , N2 > , N1 >
    • vector< vector< vector< double > > >
    • vector< double > in einem Block, daher die Größe des Vektors ist N1*N2*N3 und die Indices werden über berechnet

    Bei allen ist es Tests ist es egal ob die Indices in der innerste komplette berechnet werden oder nur noch teilweise. Der Code ist hier zu finden:

    http://pastebin.com/DW5zEPGi
    http://pastebin.com/x6L2dkcA
    http://pastebin.com/2t1cN9U9



  • Worauf ich hinaus wollte: ein vector<vector<vector<Block> > > gleicht auf unterer Ebene eher dem da:

    Block*** blocks;
    Chunk()
    {
      blocks=new Block**[8];
      for (int i=0;i<8;i++)
      {
        blocks[i]=new Block*[8];
        for (int j=0;j<8;j++)
        {
          blocks[i][j]=new Block[8];
        }
      }
    }
    

    als dem da:

    Block  blocks[8][8][8];
    


  • Zu meinem Rechner:

    Ich kann nur sagen Schrott, Schrott und nochmals Schrott. Den kann man nicht mal mehr als Mühle bezeichnen, von daher bin ich mit 4-4,5 Sekunden vollkommen zufrieden!

    Prozessor:

    Intel Pentium 4 * 2,8 GHz und 2,5 GB RAM (Plus Grafikkarte mit 1 GB VRAM)

    Noch Fragen???


Anmelden zum Antworten