   Help with a performance bottleneck?
  I'm writing a loader for Blender style .obj models.  It's still early stage and right now I'm trying to figure out what appears to be a bottleneck in performance.  I know performance is not a HUGE issue but it's something I would like to streamline and it would be a learning experience either way.I wrote 2 loaders to check the performance difference between parsing the file as I load it from the HD (the first code snippet) and loading the file contents into memory and parsing it from there (the second code snippet.)  The performance difference is negligible. Parsing while loading from the HD.bool LoadObjFile(std::string FilePath, cObjModel *Model){std::ifstream InputFile (FilePath.c_str());std::string Buffer;if (InputFile.is_open()){bool Normals = false,Texcoords = false,Vertexs = false;int v1, v2, v3, // v, t, nt1, t2, t3,n1, n2, n3;Model->NumOfVerts = 0;while (getline(InputFile, Buffer)){std::istringstream line(Buffer);std::string line_t, Mat_Lib;if (Buffer.substr(0, 6) == "mtllib"){line >> line_t >> Mat_Lib;Model->MaterialLib = Mat_Lib;}if (Buffer.substr(0,2) == "vn"){float x, y, z;Normals = true;line >> line_t >> x >> y >> z;Model->Normal.push_back( cObjNormal(x, y, z) );}else if (Buffer.substr(0,2) == "vt"){float x, y;Texcoords = true;line >> line_t >> x >> y;Model->TexCoord.push_back( cObjTexCoord(x, y) );} else if (Buffer.substr(0,1) == "v"){float x, y, z;Vertexs = true;line >> line_t >> x >> y >> z;Model->Vertex.push_back( cObjVertex(x, y, z) );Model->NumOfVerts += 1;}else if (Buffer.substr(0, 1) == "f"){Model->Face.push_back( cObjFace() );char * CharStr = new char [Buffer.size()+1];strcpy (CharStr, Buffer.c_str());if (Vertexs && Texcoords && Normals){sscanf(CharStr, "f %d/%d/%d %d/%d/%d %d/%d/%d", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().TexCoord.push_back(t1-1);Model->Face.back().Normal.push_back(n1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().TexCoord.push_back(t2-1);Model->Face.back().Normal.push_back(n2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().TexCoord.push_back(t3-1);Model->Face.back().Normal.push_back(n3-1);}else if (Vertexs && Texcoords){sscanf(CharStr, "f %d/%d %d/%d %d/%d", &v1, &t1, &v2, &t2, &v3, &t3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().TexCoord.push_back(t1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().TexCoord.push_back(t2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().TexCoord.push_back(t3-1);}else if (Vertexs && Normals){sscanf(CharStr, "f %d//%d %d//%d %d//%d", &v1, &n1, &v2, &n2, &v3, &n3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().Normal.push_back(n1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().Normal.push_back(n2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().Normal.push_back(n3-1);}else if (Vertexs){sscanf(CharStr, "f %d %d %d", &v1, &v2, &v3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().Vertex.push_back(v3-1);}delete[] CharStr;}}InputFile.close();return true;}else{std::cout << "Failed to load file: " << FilePath << std::endl;return false;}}Parsing from memory.bool LoadMemblockObjFile(std::string FilePath, cObjModel *Model){std::ifstream InputFile (FilePath.c_str(), std::ios::in|std::ios::binary|std::ios::ate);std::string Buffer;bool Normals = false,Texcoords = false,Vertexs = false;int v1, v2, v3, // v, t, nt1, t2, t3,n1, n2, n3;if (InputFile.is_open()){std::ifstream::pos_type size;size = InputFile.tellg();char * memblock;int TempSize = static_cast<int>(size) + 1;memblock = new char [TempSize];InputFile.seekg (0, std::ios::beg);InputFile.read (memblock, size);InputFile.close();memblock[size] = '\0';std::string Buffer = "";for (int i = 0; i <= size; i++){if (memblock == '\n'){std::istringstream line(Buffer);std::string line_t, Mat_Lib;if (Buffer.substr(0, 6) == "mtllib"){line >> line_t >> Mat_Lib;Model->MaterialLib = Mat_Lib;}if (Buffer.substr(0,2) == "vn"){float x, y, z;Normals = true;line >> line_t >> x >> y >> z;Model->Normal.push_back( cObjNormal(x, y, z) );}else if (Buffer.substr(0,2) == "vt"){float x, y;Texcoords = true;line >> line_t >> x >> y;Model->TexCoord.push_back( cObjTexCoord(x, y) );} else if (Buffer.substr(0,1) == "v"){float x, y, z;Vertexs = true;line >> line_t >> x >> y >> z;Model->Vertex.push_back( cObjVertex(x, y, z) );Model->NumOfVerts += 1;}else if (Buffer.substr(0, 1) == "f"){Model->Face.push_back( cObjFace() );char * CharStr = new char [Buffer.size()+1];strcpy (CharStr, Buffer.c_str());if (Vertexs && Texcoords && Normals){sscanf(CharStr, "f %d/%d/%d %d/%d/%d %d/%d/%d", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().TexCoord.push_back(t1-1);Model->Face.back().Normal.push_back(n1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().TexCoord.push_back(t2-1);Model->Face.back().Normal.push_back(n2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().TexCoord.push_back(t3-1);Model->Face.back().Normal.push_back(n3-1);}else if (Vertexs && Texcoords){sscanf(CharStr, "f %d/%d %d/%d %d/%d", &v1, &t1, &v2, &t2, &v3, &t3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().TexCoord.push_back(t1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().TexCoord.push_back(t2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().TexCoord.push_back(t3-1);}else if (Vertexs && Normals){sscanf(CharStr, "f %d//%d %d//%d %d//%d", &v1, &n1, &v2, &n2, &v3, &n3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().Normal.push_back(n1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().Normal.push_back(n2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().Normal.push_back(n3-1);}else if (Vertexs){sscanf(CharStr, "f %d %d %d", &v1, &v2, &v3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().Vertex.push_back(v3-1);}delete[] CharStr;}Buffer = "";}else{Buffer += memblock;}}delete[] memblock;return true;}{std::cout << "Failed to load file: " << FilePath << std::endl;return false;}}Now I have a fairly good idea where the bottleneck is.  If you remove the following code from the previous 2 examples it knocks down the loading time significantly.  When loading 2562 verticies with normals and texcoords and then organizing them with the face information it takes roughly 3 seconds with both loaders.  Remove the face organization code and it drops to roughly .5 seconds each.  So the following code snippet is adding almost 2.5 seconds to the process.else if (Buffer.substr(0, 1) == "f"){Model->Face.push_back( cObjFace() );char * CharStr = new char [Buffer.size()+1];strcpy (CharStr, Buffer.c_str());if (Vertexs && Texcoords && Normals){sscanf(CharStr, "f %d/%d/%d %d/%d/%d %d/%d/%d", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().TexCoord.push_back(t1-1);Model->Face.back().Normal.push_back(n1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().TexCoord.push_back(t2-1);Model->Face.back().Normal.push_back(n2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().TexCoord.push_back(t3-1);Model->Face.back().Normal.push_back(n3-1);}else if (Vertexs && Texcoords){sscanf(CharStr, "f %d/%d %d/%d %d/%d", &v1, &t1, &v2, &t2, &v3, &t3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().TexCoord.push_back(t1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().TexCoord.push_back(t2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().TexCoord.push_back(t3-1);}else if (Vertexs && Normals){sscanf(CharStr, "f %d//%d %d//%d %d//%d", &v1, &n1, &v2, &n2, &v3, &n3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().Normal.push_back(n1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().Normal.push_back(n2-1);Model->Face.back().Vertex.push_back(v3-1);Model->Face.back().Normal.push_back(n3-1);}else if (Vertexs){sscanf(CharStr, "f %d %d %d", &v1, &v2, &v3);Model->Face.back().Vertex.push_back(v1-1);Model->Face.back().Vertex.push_back(v2-1);Model->Face.back().Vertex.push_back(v3-1);}delete[] CharStr;}This section of the code is written as is for ease of reading as I'm still pretty new to C/C++.  So any thoughts on how I could improve performance?Thanks in advance.   
  Low-hanging fruit first:Buffer.substr(0, 1) == "f"This creates one string for each comparison. While state machine would be ideal, it's easy to observe you only need to check Buffer[0] and Buffer[1], then perhaps verify and skip the rest.Quote:char * CharStr = new char [Buffer.size()+1];strcpy (CharStr, Buffer.c_str());if (Vertexs && Texcoords && Normals){  sscanf(CharStr, "f %d/%d/%d %d/%d/%d %d/%d/%d", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);Don't copy the string. And sscanf might be slower and input streams, but probably suitable for this.sscanf(Buffer.c_str() + 1, ...)It does the same, but doesn't make an extra copy.And if this is compiled under Visual Studio, disable debug iterators, namely _SECURE_SCL and _HAS_ITERATOR_DEBUGGING.But a much bigger question is - why does obj loader need to be fast? If performance matters, converting it something with known element counts and in binary format will probably be the first choice anyway.   ;  hiText file processing is generally not that fast - stick to binary for best performance.You use a lot of push_back() calls that may cause a lot of reallocations of internal buffers. Try to determine the required size for each container early on to reserve() enough space, maybe this will give you some performance boost.E: As Antheus mentioned, you might want to useBuffer.compare(0, 2, "vt", 2);instead of Buffer.substr(0,2) == "vt";  Performance is an issue where a relatively small model is taking 3+ seconds to load.  Beyond that it's all about the learning experience.I will try your suggestions though.  Thank you.   ;Quote:Original post by ahlywogPerformance is an issue where a relatively small model is taking 3+ seconds to load.In addition to the other details, the primary cause is that you have a file format that's badly designed for efficient loading:1) use a binary format2) in that format include meta data so you can properly .reserve() on your containers so that the memory is allocated all in one chunk and they will then also not need to realloc and copy data around.If you want to retain the text format for ease of editing, then create a standalone console program to convert the text file into a binary file and have your engine continue to just load the binary files.String parsing is basically the slowest thing that you can do in any language.Additionally, I would suggest that if you want to get serious about performance, you really should be using a profiling tool (like VTune). Trying to guess what's making your app slow is never going to work in the long term.-me   ;  Biggest issue is you aren't calling reserve() on your vectors, causing a *lot* of reallocations. My guess is most of the work is done on this line:Model->Face.push_back( cObjFace() );CObjFace() objects also contain STL containers, so not only are you reallocating that object, but also the objects inside it. Very inefficient.Next you should be aware that pushing back a new cObjFace() invokes the object's default constructor, then it's copy constructor, then destructs the original object. cObjFace() contains STL containers - probably vectors but can't tell from your code, and compilers (at least Visual Studio) like to default allocate memory to vectors, which results in pointless new/delete calls. If you explicitly construct vector in the object's initialiser list, you can specify a zero size and it won't allocate memory.cObjFace::cObjFace() :Vertex(0),TexCoord(0),Normal(0){   // Rest of constructor}This works in Visual Studio but the standard doesn't require it to. Certain implementations might allocate a minimum number of elements no matter what, or choose not to default allocate any at all.Now you should likewise call reserve on the interior containers so they don't reallocate either. I'm not sure why you decided to use a separate container for vectors, normals and texture indices though. It's more efficient to place them into the one structure.You're also dynamically allocating and then releasing CharStr on every iteration, when you could be reusing it instead. If you can't easily determine the maximum size, the easiest solution is to use a c++ container (stringstream, etc) and let it grow during runtime until it reaches a point where it's big enough to hold anthing for the remaining iterations of the loop.   ;  These are the structs I am using.  Does that clarify things a little?struct cObjVertex {cObjVertex(float f1, float f2, float f3): x(f1), y(f2), z(f3) {}float x, y, z;};struct cObjNormal {cObjNormal(float f1, float f2, float f3): x(f1), y(f2), z(f3) {}float x, y, z;};struct cObjTexCoord {cObjTexCoord(float f1, float f2): u(f1), v(f2) {}float u, v;};struct cObjFace {std::vector<int> Vertex;std::vector<int> Normal;std::vector<int> TexCoord;};struct cObjModel {int NumOfVerts;std::string MaterialLib;std::vector<cObjVertex> Vertex;std::vector<cObjNormal> Normal;std::vector<cObjTexCoord> TexCoord;std::vector<cObjFace> Face;};Thanks for all the input so far.  I'm taking notes.   ;Original post by ahlywogstruct cObjFace {std::vector<int> Vertex;std::vector<int> Normal;std::vector<int> TexCoord;};Last time I tried something like this I got terrible performance by using std::vector. If you are having to create a lot of cObjFace's, then you are likely to run into the same problem I had, where all the vector initializations were killing the performance.I didn't actually need std::vector's dynamic size, so I solved the problem by switching to std::array. Is a cObjFace just a single triangle or quad? If so, then you don't need a dynamic container either.;  I believe, right now, it's geared for triangles but I've been throwing around the idea of expanding it to quads as well.  As broad a coverage as possible I guess.   ;Quote:Original post by ahlywogI believe, right now, it's geared for triangles but I've been throwing around the idea of expanding it to quads as well.  As broad a coverage as possible I guess.Well I saw a decrease in speed of a bout a factor of ten when using vectors instead of array in my application.   