Schneller speichern und laden
-
Na das selbe. Daten vorbereiten und in dann auf ein mal schreiben.
-
Guck erstmal ob es nicht an deinen get_ID()/set_ID() Funktionen liegt.
z.B. mal so porbieren:void World::save(std::ofstream& file) { unsigned int tmp = 0; for (unsigned int i = 0; i < 512; i++) { for (unsigned int j = 0; j < 512; j++) { for (unsigned int k = 0; k < 128; k++) { tmp ^= this->get_ID(i, j, k); tmp ^= this->get_Sub_ID(i, j, k); tmp ^= this->get_direction(i, j, k); } } } file.put((unsigned char) (tmp >> 24)); file.put((unsigned char) (tmp >> 16)); file.put((unsigned char) (tmp >> 8)); file.put((unsigned char) (tmp >> 0)); }Das wird die Geschwindigkeit sein die du maximal erreichen kannst. Wenn das schon zu langsam ist, dann musst du erstmal get_ID/get_Sub_ID/get_direction optimieren.
-
100 s für 96 MB? Da ist klingt falsch. Flusht (z.B. durch ein endl) du zwischendurch versehentlich? Bei mir schreibe ich diese Datenmenge in deutlich weniger als 10 Sekunden, ebenfalls mit sehr komplexen Formatierungen
-
SeppJ schrieb:
100 s für 96 MB? Da ist klingt falsch. Flusht (z.B. durch ein endl) du zwischendurch versehentlich? Bei mir schreibe ich diese Datenmenge in deutlich weniger als 10 Sekunden, ebenfalls mit sehr komplexen Formatierungen
put macht afair ein je ein lock. Das könnte echt aufs Gemüse gehen.
-
ich will ja auch nicht nach jedem byte flushen, aber wie mache ich das am besten?
Edit:
Kleiner Fortschritt: Nur noch 78 sec mit folgendem Code:
void World::save(std::ofstream& file) { unsigned int tmp; Block_Container* block; Chunk* chunk; vector<char> buf(196608); for(unsigned int ci = 0; ci < 64; ci++) { for(unsigned int cj = 0; cj < 64; cj++) { for(unsigned int ck = 0; ck < 16; ck++) { chunk = &this->chunks[ci][cj][ck]; for(unsigned int i = 0; i < 8; i++) { for(unsigned int j = 0; j < 8; j++) { for(unsigned int k = 0; k < 8; k++) { block = &chunk->blocks[i][j][k]; tmp = block->get_ID(); file.put((unsigned char) (tmp >> 8)); file.put((unsigned char) tmp); file.put((unsigned char) ((block->get_Sub_ID() << 2) | block->get_direction())); } } } } } } }
-
Machst du da nicht irgendwie das Gleiche wie vorher?
-
Am schnellsten wäre immer noch, für ein ordentliches Speicherlayout zu sorgen und dann die einzelnen Chunks komplett in einem Rutsch zu schreiben.
-
Da du "buf" bereits angelegt hast, solltest du ihn jetzt auch nutzen.
Wenn du damit fertig bist, empfiehlt es sich auch, einen schnellen Kompressionsalgorithmus wie LZO anzuwenden, da deine 100 MB wahrscheinlich weitgehend redundant sind.
-
Des Weiteren solltest du dein Chunk-Format dahingehend optimieren, dass keine Verschiebungen von Daten innerhalb der Datei vorkommen wenn neue Chunks generiert werden. cooky und ich haben das damals über eine zweite Indizierungs-Datei gelöst.
-
Die erinnerung schrieb:
ich will ja auch nicht nach jedem byte flushen, aber wie mache ich das am besten?
Edit:
Kleiner Fortschritt: Nur noch 78 sec mit folgendem Code:
void World::save(std::ofstream& file) { unsigned int tmp; Block_Container* block; Chunk* chunk; vector<char> buf(196608); for(unsigned int ci = 0; ci < 64; ci++) { for(unsigned int cj = 0; cj < 64; cj++) { for(unsigned int ck = 0; ck < 16; ck++) { chunk = &this->chunks[ci][cj][ck]; for(unsigned int i = 0; i < 8; i++) { for(unsigned int j = 0; j < 8; j++) { for(unsigned int k = 0; k < 8; k++) { block = &chunk->blocks[i][j][k]; tmp = block->get_ID(); file.put((unsigned char) (tmp >> 8)); file.put((unsigned char) tmp); file.put((unsigned char) ((block->get_Sub_ID() << 2) | block->get_direction())); } } } } } } }Da denke ich an
void World::save(std::ofstream& file) { unsigned int tmp; Block_Container* block; Chunk* chunk; vector<char> buf(196608); for(unsigned int ci = 0; ci < 64; ci++) { for(unsigned int cj = 0; cj < 64; cj++) { for(unsigned int ck = 0; ck < 16; ck++) { chunk = &this->chunks[ci][cj][ck]; unsigned char buffer[8*8*8*3];//oder stringstream oder vector oder array unsigned char* writePos=buffer; for(unsigned int i = 0; i < 8; i++) { for(unsigned int j = 0; j < 8; j++) { for(unsigned int k = 0; k < 8; k++) { block = &chunk->blocks[i][j][k]; tmp = block->get_ID(); *writepos++=(unsigned char) (tmp >> 8)); *writepos++(unsigned char) tmp); *writepos++(unsigned char) ((block->get_Sub_ID() << 2) | block->get_direction())); } } } file.write(buffer,sizeof(buffer)); } } } }
-
So konnte ich auf 40 sec reduzieren!
void World::save(std::ofstream& file) { unsigned int tmp; Block_Container* block; Chunk* chunk; vector<char> buf(100663296); int count = 0; for(unsigned int ci = 0; ci < 64; ci++) { for(unsigned int cj = 0; cj < 64; cj++) { for(unsigned int ck = 0; ck < 16; ck++) { chunk = &this->chunks[ci][cj][ck]; for(unsigned int i = 0; i < 8; i++) { for(unsigned int j = 0; j < 8; j++) { for(unsigned int k = 0; k < 8; k++) { block = &chunk->blocks[i][j][k]; tmp = block->get_ID(); buf[count] = ((unsigned char) (tmp >> 8)); buf[count + 1] = ((unsigned char) tmp); buf[count + 2] = ((unsigned char) ((block->get_Sub_ID() << 2) | block->get_direction())); count += 3; } } } } } } file.write(buf.data(), buf.size()); }Geht da mehr?
-
Die erinnerung schrieb:
Geht da mehr?
Nein. Ob man in 1k großen Blöcken schreibt oder in 64k großen, das macht nur noch ein Prozent aus. Wir sind jetzt bei 1.5k. Du solltest damit schon dicht an der Maximalgeschwindigkeit sein.
Was jetzt Zeit frißt, ist nicht mehr das Speichern, sondern es müssen Deine Zugriffe zum Auslesen der Daten sein.
Schon alle Compiler-Optimierungen an?edit: Ups, hast ja alles auf einmal geschrieben. Dann erst recht. Schneller schreiben geht "eigentlich" nicht.
Die reine Schreibzeit ist bei mir
void save(std::ofstream& file) { vector<char> buf(100663296); int count = 0; file.write(buf.data(), buf.size()); }um 60 Millisekunden.
Der schreibt ja erstmal ins RAM, also den Festplattencache und dann erst die Daten raus auf die Platte, wenn die lahme Platte mal Lust hat, die passenden Sektoren vorbeizuschicken.
Es muß am Auslesen liegen.
-
Mit Sicherheit geht da mehr. Nur mal zur Orientierung, das ganze sollte nicht viel länger als eine Sekunde dauern - und auch das nur auf einem Billig-Netbook.
Zeig doch mal etwas mehr Code, z.B. die Implementierung der Block-Methoden.
Edit: auch alles auf einmal schreiben muss bei größeren Datenmengen wie hier nicht unbedingt vorteilhaft sein. Bei kleineren Chunks kann sich das OS nebenbei darum kümmern, während du den nächsten vorbereitest. Probiere es mal mit 64 write()-Aufrufen.
-
WTF!!!!!!!!
Kaum hau ich optimierungen rein, schon bin ich bei 4 sekunden...Das ist schnell genug. Danke!
-
Du hast ohne Optimierungen gemessen?

Übrigens, ich hab auch mal gemessen und mit folgender Dummy-Klasse und 64 Schreibeinheiten (also nach jedem Durchlauf der äußeren Schleife) dauert das bei mir 0.2 Sekunden:struct World { struct Chunk { struct Block { Block() : id(1), subID(2), direction(3) {} int id; int subID; int direction; int get_ID() const {return id;} int get_Sub_ID() const {return subID;} int get_direction() const {return direction;} }; Block blocks[8][8][8]; }; Chunk chunks[64][64][16]; void save(std::ofstream& file); };Wenn dir danach ist, kannst du evtl. also auch noch mehr rausholen.
-
Hast du eigentlich auch mal dein ursprüngliches Programm mit Optimierungen ausprobiert? Ich kann mir nämlich nicht vorstellen, dass put an sich so furchtbar langsam ist.
-
Die erinnerung schrieb:
ich will ja auch nicht nach jedem byte flushen, aber wie mache ich das am besten?
Edit:
Kleiner Fortschritt: Nur noch 78 sec mit folgendem Code:
void World::save(std::ofstream& file) { unsigned int tmp; Block_Container* block; Chunk* chunk; vector<char> buf(196608); for(unsigned int ci = 0; ci < 64; ci++) { for(unsigned int cj = 0; cj < 64; cj++) { for(unsigned int ck = 0; ck < 16; ck++) { chunk = &this->chunks[ci][cj][ck]; for(unsigned int i = 0; i < 8; i++) { for(unsigned int j = 0; j < 8; j++) { for(unsigned int k = 0; k < 8; k++) { block = &chunk->blocks[i][j][k]; tmp = block->get_ID(); file.put((unsigned char) (tmp >> 8)); file.put((unsigned char) tmp); file.put((unsigned char) ((block->get_Sub_ID() << 2) | block->get_direction())); } } } } } } }this->chunks[ci][cj][ck]machts langsam. Diese Dereferenzierung kann stark beschleunigt werden, in dem Du in den Schleifen davor schon die Unterblöcke berechnest:// Achtung: Pseudo-Code, subchunk1 und subchunk2 müssen richtig definiert werden... for( ci ) { subchunk1 = this->chunks[ci]; for( cj ) { subchunk2 = subchunk1[ci]; for( ck ) { chunk = subchunk2[ck]; } } }Das gilt natürlich auch für
chunk->blocks[i][j][k]
-
Nein, habe ich nicht, aber ich vermute es würde ca. 10 Sekunden dauern, da ich durch das optimieren ca. um den Faktor 10 schneller bin.
Ich verstehe aber auch nicht, warum der Aufruf so lange dauert.
Ich sammele mal den Code zusammen:get-Id-Funktion mit Infos:
//========== Klasse: ========== class Block_Container { private: static std::map<Block_ID, Block*> BLOCKS; unsigned short direction; Block* block; public: Block_Container(); ~Block_Container(); void set_ID(unsigned short ID); void set_ID(unsigned short ID, unsigned short Sub_ID); void set_ID(unsigned short ID, unsigned short Sub_ID, unsigned short direction); void set_ID(Block_ID ID); void set_ID(Block_ID ID, unsigned short direction); void set_direction(unsigned short direction); unsigned short get_ID() const; unsigned short get_Sub_ID() const; Block_ID get_Block_ID() const; unsigned short get_direction() const; friend std::ostream& operator<<(std::ostream& Stream, const Block_Container block); friend std::ostream& operator<<(std::ostream& Stream, const Block_Container* block); friend World; }; //========== Funktion: ========== unsigned short Block_Container::get_ID() const { return this->block->get_ID().ID; }Das get_ID (Hierbei handelt es sich um Polymorphie!)
//========== Klasse: ========== class Block { public: Block(void); ~Block(void); virtual Block* clone(); virtual Block_ID get_ID(); }; //========== Funktion: ========== Block_ID Block::get_ID() { return Block_ID(0); }Und noch Block_ID:
class Block_ID { public: unsigned short ID; unsigned short Sub_ID; Block_ID(); Block_ID(unsigned short ID); Block_ID(unsigned short ID, unsigned short Sub_ID); ~Block_ID(); bool operator< (Block_ID data) const; bool operator== (Block_ID data) const; bool operator!= (Block_ID data) const; friend std::ostream& operator<<(std::ostream& Stream, const Block_ID Block); friend std::ostream& operator<<(std::ostream& Stream, const Block_ID* Block); }; Block_ID::Block_ID() { Block_ID::ID = 0; Block_ID::Sub_ID = 0; } Block_ID::Block_ID(unsigned short ID) { if((ID < 0) || (ID > 65535)) { throw(BF_Error(1, 4)); } Block_ID::ID = ID; Block_ID::Sub_ID = 0; } Block_ID::Block_ID(unsigned short ID, unsigned short Sub_ID) { if((ID < 0) || (ID > 65535)) { throw(BF_Error(1, 4)); } if((Sub_ID < 0) || (Sub_ID > 63)) { throw(BF_Error(1, 5)); } Block_ID::ID = ID; Block_ID::Sub_ID = Sub_ID; } Block_ID::~Block_ID() { } bool Block_ID::operator< (Block_ID data) const { if(Block_ID::ID == data.ID) { return Block_ID::Sub_ID < data.Sub_ID; } return Block_ID::ID < data.ID; } bool Block_ID::operator== (Block_ID data) const { return (Block_ID::ID == data.ID) && (Block_ID::Sub_ID == data.Sub_ID); } bool Block_ID::operator!= (Block_ID data) const { return (Block_ID::ID != data.ID) || (Block_ID::Sub_ID != data.Sub_ID); } std::ostream& operator<<(std::ostream& Stream, const Block_ID Block) { if(Block.Sub_ID == 0) { return Stream << Block.ID; } return Stream << Block.ID << ":" << Block.Sub_ID; } std::ostream& operator<<(std::ostream& Stream, const Block_ID* Block) { if(Block->Sub_ID == 0) { return Stream << Block->ID; } return Stream << Block->ID << ":" << Block->Sub_ID; }Ich verstehe nicht, wieso das so lange dauert...
Edit:
Wie definiere ich die richtig?
(So gehts nicht:Chunk* (chunk_2d[64][16]);, wie sonst? (Ohne die Klammer gehts auch nicht!)
-
Was ist den der Typ von World::chunks?
-
Na gut, damit sind die vier Sekunden nicht mehr verwunderlich.
Erst einmal, wenn du Funktionen nicht in der gleichen Übersetzungseinheit definierst, wird dir das ohne WPO/LTO nicht geinlined. Simple Funktionen wie Getter sollte man in den Header verlagern, sofern das möglich und sinnvoll ist.
Die Indirektion in Block_Container und der virtuelle Funktionsaufruf wird der Sache den Rest geben. Müssen deine Blöcke wirklich polymorph sein?
Falls ja, würde ich es einfach dabei belassen.