Programm hängt sich in einer For-Schleife beim Erzeugen von Objekten auf.
-
Welche schlechte Erfahrungen hast du den mit bitweisen Manipulationen bei int, short, ... gemacht? Und std::bitset spart kein einziges Byte, denke ich. Das ist intern auch nur ein array von unsigned chars.
EDIT: Warum machst du bitweise Manipulationen auf den Ḱeys ein mapWas soll das bringen?
-
pyhax schrieb:
Und std::bitset spart kein einziges Byte, denke ich. Das ist intern auch nur ein array von unsigned chars.
Auch wenn es vom Standard nicht erzwungen wird, so liegt die interne Datenspeicherung in einer gepackten Form a la vector<bool> aufgrund der bitset-Schnittstelle doch sehr nahe. Mindestens die GCC-Implementierung verwendet daher intern ein unsigned long Array (dessen Länge nur bei sehr vielen Bits größer als 1 wird). Bitset zu benutzen ist daher äquivalent zum Bits selber manipulieren*, außer dass jemand anderes für einen schon die Fleißarbeit übernommen hat.
*: Egal ob Bitset oder Eigenbau: Die Technik hat Vor- und Nachteile!
-
SeppJ schrieb:
Mindestens die GCC-Implementierung verwendet daher intern ein unsigned long Array.
Das heißt, ein bitset hat mindestens die Größe (beim GCC) sizeof(unsigned long)?
-
*weißt darauf hin, dass Vergrößern des Speicherbereichs in einer unordererd map im worst case O(n^2) ist und verzieht sich dann wieder aus dem Thread*
-
Es gibt keine Garantien, aber ausprobieren kann das jeder selbst.
int main(int argc, char** argv) { std::bitset<1> bt; int t; std::cout << sizeof(bt) << " " << sizeof(t) << std::endl; }
-
pyhax schrieb:
Das heißt, ein bitset hat mindestens die Größe (beim GCC) sizeof(unsigned long)?
Zumindest auf meinem 64-Bit System. Die Kommentare im Quelltext deuten darauf hin, dass die natürliche Wortbreite des Prozessors benutzt wird (was irgendwie naheliegend ist), jedoch ist da andererseits das unsigned long fest eingebaut. Da müsste man mal mit einem 32-Bit System vergleichen.
-
SeppJ schrieb:
unsigned long
unsigned long ist ja auch nur mindestens 32 bit breit, erst long long ist auch auf 32 bit Systemen 64 bit breit.
-
Zur Frage was bitweise manipulationen mit dem Key zu tun haben:
Der Key ist eine 3-dimensionale Koordinate, die aus x, y und z-Wert besteht. um Speicher zu sparen, komprimiere ich diese Informationen in einer Zahl, die als eindeutige Position dient. Damit ich aber aus den Koordinaten einen einzigen Wert machen kann, muss ich die Bitweise Manipulation anwenden. Denn ich schneide die einzel Koordinaten zurecht und verschiebe sie an die richtige Stelle.
x-Koordinate hat 9 Bit
y-Koordinate hat 9 Bit
z-Koordinate hat 7 Bit===>
Eindeutige Position hat 25 Bit
Ursprünglich waren bei allen jeweils ein Bit mehr geplant, ist dann aber zu groß!
Ich werde mein problem jetzt ganz einfach lösen:
ich lege mir Überquadrate an, die eine gewisse Menge von Blöcken in sich tragen und die Welt verwaltet diese Überquadrate (Chunks) dann.
Vielen Dank für eure Hilfe!
Und vielen, vielen Dank an otze. ich hatte sowas schon vermutet, aber war mir nicht sicher!
-
@Die erinnerung
Und 8bit/8bit/8bit wäre nicht gelaufen? ^^
Wie auch immer, wenn die Welt nur halbwegs gefüllt ist, ist das eine Blöde Idee. Rechne mal nach, wie viel Platz du bei einer halbgefüllten Welt brauchst, die 128x128x128 Einheiten groß ist:
Keys: 25 * 128 * 128 * 128 / 2 / 8: 3276800 byte
Values (1 byte / Wert): 128 * 128 * 128 / 2: 1048576 byte
Machen zusammen 4325376 byte.Ok, jetzt führen wir mal einen "Nix" Block ein, und speichern die Blockpositionen implizit durch den Index:
Values (1 byte / Wert): 128 * 128 * 128: 2097152 byteOh, hm. Ist ja viel weniger Speicher. Und zwar viel viel weniger. Denn man braucht keine Pointer mehr. Und den Overhead durch Bitgefrickel kann man jetzt auch vergessen.
Du siehst, manchmal lohnt es, einfach das Problem zu beschreiben.
-
Ich verstehe deine Rechnung nicht.
Wenn ein Block nicht vorhanden ist, dann verbraucht der kein Speicherplatz. Das einzige was bei einer vollkommen leeren Welt noch Speicherplatz braucht, ist sind die Übercontainer und das ist nicht viel.
Ich glaube du hast in deienr Rechnung vergessen, dass der Index in einem Container auch Speicherplatz braucht.
Und zur Frage was meine schlechten Erfahrung mit int und co sind:
Ich hatte schon einige Projekte bei denen ich mit bitweise Manipulationen arbeiten musste. Bei allen haben sich binäre Fehler eingeschlichen!!!
Ein Bespiel:
Ich wollte das ganze ursprünglich mit einem char-Array machen.
Beispiel (frei Erfunden das Problem ist aber real!):
Ich hatte zwei Werte. Einer 9 Bit der andere 7 Bit.Beispeil:
000010110 0001011
im Char Array:
00001011 00001011Wenn ich aber das haben
000110111 0011011
dann ist mein Chr array:
11111011 11111011Und das ist falsch!
Mit bitset funktiniert das Fehlerfrei!
-
Die erinnerung schrieb:
Ich verstehe deine Rechnung nicht.
In cookys rechnung gibt es einfach keinen Index. Auch ein leerer Block, der eigentlich nicht vorhanden ist, wird gespeichert. Die Koordinate des Blocks ist der Index in einem Array/Vector.
Ein Chunk könnte damit im Prinzip einfach ein Array für die Blöcke enthalten:
Block blocks[sizeX][sizeY][sizeZ];
Wobei der Block mit der Koordinate (X/Y/Z) nun einfach bei blocks[X][Y][Z] liegt. Somit sparst du dir Indizes und Dereferenzierungen. Des Weiteren hat jeder Chunk eine konstante Größe, was Allokationen und Deallokationen spart.
-
Die erinnerung schrieb:
Ich verstehe deine Rechnung nicht.
Dann lies sie noch mal.
Die erinnerung schrieb:
Wenn ein Block nicht vorhanden ist, dann verbraucht der kein Speicherplatz. Das einzige was bei einer vollkommen leeren Welt noch Speicherplatz braucht, ist sind die Übercontainer und das ist nicht viel.
Für eine komplett leere Welt ist das nicht optimal. Fragt sich, ob das realistisch ist.
Die erinnerung schrieb:
Ich glaube du hast in deienr Rechnung vergessen, dass der Index in einem Container auch Speicherplatz braucht.
Nein, siehe Pi.
-
Ah!
Ich dachte immer, Arrays brauchen auch Speicher für den Index. Dann ist natürlich die Variante mit den arrays besser!
Danke setzt das jetzt dann um!
-
Ich hab das jetzt umgesetzt. Weil das ganze ohne Zeiger und new nicht geht habe ich das so gemacht
Es dauert schon seine Zeit, bis alle Objekte erzeugt und geladen sind. das ist nicht das Problem. Aber es dauert sehr lange, bis sie wieder gelsöcht sind.
(Siehe Bild: http://bildupload.sro.at/p/393597.html (Belegter RAM))
Ich hab mal die Zeit gemessen. Dabei kam raus:
======================================Start===================================== Erstlle Welt X 27.343 sec Zerstoere Welt X 120.734 sec ======================================Ende======================================
Geht das nicht schneller?
Code:
main(main.cpp):
//Auschnitt World *welt = new World(); //Erzeugt eine neue Welt // ... Messungen und co... delete welt; //Löscht sie wieder
Konstruktor und Destruktor von World(World.cpp):
#include "World.h" #include <windows.h> World::World() { for(int i = 0; i < 64; i++) { for(int j = 0; j < 64; j++) { for(int k = 0; k < 16; k++) { World::chunks[i][j][k] = new Chunk(); } } } } World::~World() { for(int i = 0; i < 64; i++) { for(int j = 0; j < 64; j++) { for(int k = 0; k < 16; k++) { delete World::chunks[i][j][k]; } } } }
World-Klasse(World.h):
#include "Chunk.h" #include "Block.h" #pragma once class World { private: Chunk *chunks[64][64][16]; //Cunks public: World(); ~World(); }
Konstruktor und Destruktor von Chunk(Chunk.cpp):
#include "Chunk.h" Chunk::Chunk() { for(int i = 0; i < 8; i++) { for(int j = 0; j < 8; j++) { for(int k = 0; k < 8; k++) { Chunk::container[i][j][k] = new Block(); } } } } Chunk::~Chunk() { for(int i = 0; i < 8; i++) { for(int j = 0; j < 8; j++) { for(int k = 0; k < 8; k++) { delete Chunk::container[i][j][k]; } } } }
Chunk-Klasse(Chunk.h):
#include "Block.h" #pragma once class Chunk { private: Block* container[8][8][8]; public: Chunk(); ~Chunk(); };
(Block ist nicht wichtig. Da ist noch nichts gemacht, außer das die Klasse existiert.)
Kann man das irgendwie schneller machen???
-
Kannst du noch mal kurz den Thread zusammenfassen? Ich müsste sehr viel lesen um zu sehen, worum es überhaupt gerade geht.
Jedenfalls beim ersten Draufgucken:
Du hast da 8*8*8*64*64*16 Zeiger auf einzelne Block-Objekte, die einzeln allokiert und freigegeben werden. Und wenn ich das richtig sehe, sind die auch immer allesamt belegt, d.h. es ist Unsinn, hier überhaupt solch eine Wahnsinnsindirektion einzuführen. Mach dochChunk (*chunks)[64][64][16];
undWorld::World(): chunks = new Chunk[64][64][16]; {} World::~World() { delete[] chunks;}
Und entsprechend auch bei den Blocks. Oder nimm gleich einen
vector<std::array<std::array<Chunk,64>,64>
mit 16 Elementen, dann sparst du dir die potentiellen Fehler, verlierst aber nix.
-
Im Prinzip bin ich dabei eine Gamelibary für ein Spiel zu schreiben.
Die Welt ist 3-dimensional und ich versuche das möglichst effinzient zu machen.Zrück zu deinem Beitrag:
Wenn ich (in der .cpp-Datei)
World::chunks = new Chunk[64][64][16];
mache, dann mekert er, dass erChunk(*)[64][16]
nicht inChunk(*)[64][64][16]
umwandeln kann.
Wenn ich aber (in der .cpp-Datei)World::chunks = new Chunk[1][64][64][16];
mache, dann mekert er nicht, aber er scheint nichts zu allokieren!Kannst du mir helfen.
P.S.: mit deiner Alternative kann ich nicht viel anfangen. Kannst du mir das genauer erklären?
-
Die erinnerung schrieb:
Im Prinzip bin ich dabei eine Gamelibary für ein Spiel zu schreiben.
Die Welt ist 3-dimensional und ich versuche das möglichst effinzient zu machen.Zrück zu deinem Beitrag:
Wenn ich (in der .cpp-Datei)
World::chunks = new Chunk[64][64][16];
mache, dann mekert er, dass erChunk(*)[64][16]
nicht inChunk(*)[64][64][16]
umwandeln kann.
Wenn ich aber (in der .cpp-Datei)World::chunks = new Chunk[1][64][64][16];
mache, dann mekert er nicht, aber er scheint nichts zu allokieren!Kannst du mir helfen.
Deshalb sag ich ja, dass du die fertigen STL-Klassen benutzen sollst. Dann muss man sich nicht mit der new-Syntax rumschlagen, die, mangels Benutzung, sowieso kein Mensch auswendig kennt wie du siehst. So ist es korrekt:
int main() { int (*test) [64][16]; test = new int[64][64][16]; delete[] test; }
P.S.: mit deiner Alternative kann ich nicht viel anfangen. Kannst du mir das genauer erklären?
Das ist zu viel zu erklären für einen Forenbeitrag. Lies ein gutes(!) Buch, siehe zweiter Link in meiner Signatur. vector und array sind unendlich viel besser als new[]. Wenn dir new beigebracht wurde, ohne dass du vector kennst, dann taugt dein Lehrer nichts (kommt leider viel zu oft vor, selbst bei Lehrbüchern). new[] braucht man nie!
P.S.: Und bei den Chunks selber machst du dann gar nichts dynamisch (es ist ja hier sowieso nur eine Krücke, da ein so großes Array nicht auf den Stack passt. Es hat ja an sich statische Größe), sondern machst wirklich konkret
Block container[8][8][8];
, ganz ohne Zeiger (evtl noch std::array statt dem rohen array, falls ducontainer
zwischen Funktionen herumreichen willst). Das wird dann ungeheuer effizient, weil du effektiv nur ein einzigen new hast, das einen riesigen zusammenhängenden Block anfordert. Das spart die erstens die langsamen news und zweitens wird das viel effizienter beim Zugriff, denn verteilte Zugriffe sind einer der größten Leistungskiller überhaupt.
-
Die erinnerung schrieb:
ich versuche das möglichst effinzient zu machen.
Scherz?
Deine Lösung ist mit Abstand eine der ineffizientesten, die ich mir vorstellen kann.
-
Vector kenne ich natürlich. (Nur nicht std::arary) Ich bin mir nur nicht ganz sicher, wie genau ich das verwenden soll.
Und die anderen haben mir (indirekt) zu dieser Variante geraten.
Ich würde auch viel lieber stattChunk (*chunks)[64][64][16];
Chunk chunks[64][64][16];
schreiben. aber dann sagt er, dass irgendwo ein Runtimeerror auftritt.
Ich fange in meiner main-Funktion alles ab, aber er behauptet, dass ich den Fehler nicht abgefangen hätte.(Wenn du willst, sag ich dir genau, was passiert)
-
Das Problem ist, dass die Datenstruktur insgesamt zu groß ist für die Stackgrößen der meisten Systeme. Das kann man i.d.R. zwar hochsetzen, aber das ist ja nicht so dolle, wenn das eine Engine werden soll, die vielleicht auch mal (zumindest theoretisch) jemand anderes nutzen soll. Eine einmalige Heapallokation ist da schon ok.
Ich habe oben in meinen Beitrag noch Zusatzinfos reineditiert, während du schon geantwortet hast, die sich genau mit diesem Problem befassen.