   [C++] Constructor performance initializing 16 floats pops on top with gprof
  Hi there,I'm finalizing my library code for release v1. So after getting the code warning free (also with -Weffc++) I did a Release build with profiling (GCC 4.4.0).I haven't yet used caching, for TTF text for instance, so I didn't expect the framerate to be too high. It wasn't, but gprof told me it wasn't the font part but rather some other part of my design.Listed as most-time-consuming function was ColourRectangle(Colour const&), and third Colour(float, float, float, float).ColourRectangle holds 4 instances of Colour (for each corner of a rectangle), and Colour holds 4 floats (r, g, b, a).My design is as follows: everything that needs to be drawn is a quad, so each frame I collect all quads and put them in a queue, after collecting I draw them from the queue. Each queue item consists of a position and size of the rectangle, of ColourRectangle, of a texture and of texture coordinates. This is then used to draw a quad with OpenGL.Since not all quads need a colour, the ColourRectangle is often not passed. However, there does need to be a ColourRectangle in the queue items. So it is constructed at it's default values (white).So, for about 100 quads per frame, there is 4 * 4 floats constructed at 1.0fHow can this take 12% (ColourRectangle) + 5% (Colour) = 17% of the execution time according to gprof, and is therefore the slowest component?Are floating points still pretty heavy? Also copy constructing of various float-structs seems to be costly...any ideas?   
  Without knowing the rest of your code (or a minimal self-contained testcase), every answer is only a blind shot into the fog.   ;  You are correct:It's basically a cache, it caches all draw calls into a std::queue, and draws it every frame. When changes occur, the whole GUI hierarchy (it's a GUI library) is traversed and added to the (emptied) queue.  class OpenGL: public Cache  {  private: struct Quad {Rectangle coordinates;ColourRectangle colours;Resources::TextureHandles::ConstOpenGLPointer texture_handle;TextureCoordinates texture_coordinates;Quad(const Rectangle &i_coordinates = Rectangle(), const ColourRectangle &i_colours = ColourRectangle(), const Resources::TextureHandles::ConstOpenGLPointer &i_texture_handle = NULL, const TextureCoordinates &i_texture_coordinates = TextureCoordinates()); };  public: ... ... ... //////////////// void quad(const Rectangle &i_coordinates = Rectangle(), const ColourRectangle &i_colours = ColourRectangle(), const Resources::ConstTextureHandlePointer &i_texture_handle = NULL, const TextureCoordinates &i_texture_coordinates = TextureCoordinates()) const; void quad(const Rectangle &i_coordinates = Rectangle(), const Resources::ConstTextureHandlePointer &i_texture_handle = 0, const TextureCoordinates &i_texture_coordinates = TextureCoordinates()) const; //////////////// void render() const;  private: mutable std::queue<Quad> quads;  };Using the quad() methods new quads are added. The render() method renders it.The quad() methods look like this:  void OpenGL::quad(const Rectangle &i_coordinates, const ColourRectangle &i_colours, const Resources::ConstTextureHandlePointer &i_texture_handle, const TextureCoordinates &i_texture_coordinates) const  {if (i_texture_handle){    const Resources::TextureHandles::ConstOpenGLPointer &t_texture_handle = dynamic_cast<const Resources::TextureHandles::OpenGL *> (i_texture_handle.pointer());    if (!t_texture_handle)    {   ERROR("invalid type of 'Resources::TextureHandle', must be a 'Resources::TextureHandles::OpenGL'");    }    quads.push(Quad(i_coordinates, i_colours, t_texture_handle, i_texture_coordinates));}else{    quads.push(Quad(i_coordinates, i_colours));}  }  void OpenGL::quad(const Rectangle &i_coordinates, const Resources::ConstTextureHandlePointer &i_texture_handle, const TextureCoordinates &i_texture_coordinates) const  {if (i_texture_handle){    const Resources::TextureHandles::ConstOpenGLPointer &t_texture_handle = dynamic_cast<const Resources::TextureHandles::OpenGL *> (i_texture_handle.pointer());    if (!t_texture_handle)    {   ERROR("invalid type of 'Resources::TextureHandle', must be a 'Resources::TextureHandles::OpenGL'");    }    quads.push(Quad(i_coordinates, ColourRectangle(), t_texture_handle, i_texture_coordinates));}else{    quads.push(Quad(i_coordinates));}  }To show a rough example of how most things are drawn, this is the call code to quad() when drawing a single character:  void FreeType2::draw(const ConstOperatorPointer &i_target, const Coordinate &i_coordinate, const Character &i_character) const  { i_target->quad(Rectangle(i_coordinate.x + i_character.x * m_resize, i_coordinate.y + (-i_character.y) * m_resize, i_character.w * m_resize, i_character.h * m_resize),m_texture_handle,TextureCoordinates(    Coordinate(i_character.tx, i_character.ty),    Coordinate(i_character.tw, i_character.ty),    Coordinate(i_character.tw, i_character.th),    Coordinate(i_character.tx, i_character.th)) );  }Most objects don't specify their Colour(Rectangle). But it's required for my render() code: void OpenGL::render() const  { bool enable_texture = false; GLuint current_texture = 0; glDisable(GL_TEXTURE_2D); glBegin(GL_QUADS); while (quads.size()) {const Quad &t_quad = quads.front();if (t_quad.texture_handle){    if (!enable_texture)    {   glEnd();   glEnable(GL_TEXTURE_2D);   glBegin(GL_QUADS);   enable_texture = true;    }    if (current_texture != t_quad.texture_handle->id())    {   glEnd();   glBindTexture(GL_TEXTURE_2D, t_quad.texture_handle->id());   glBegin(GL_QUADS);   current_texture = t_quad.texture_handle->id();    }    glColor4f(t_quad.colours.colour[0].r, t_quad.colours.colour[0].g, t_quad.colours.colour[0].b, t_quad.colours.colour[0].a);    glTexCoord2f(t_quad.texture_coordinates.coordinate[0].x, t_quad.texture_coordinates.coordinate[0].y);    glVertex2f(t_quad.coordinates.x, t_quad.coordinates.y);    glColor4f(t_quad.colours.colour[1].r, t_quad.colours.colour[1].g, t_quad.colours.colour[1].b, t_quad.colours.colour[1].a);    glTexCoord2f(t_quad.texture_coordinates.coordinate[1].x, t_quad.texture_coordinates.coordinate[1].y);    glVertex2f(t_quad.coordinates.x + t_quad.coordinates.w, t_quad.coordinates.y);    glColor4f(t_quad.colours.colour[2].r, t_quad.colours.colour[2].g, t_quad.colours.colour[2].b, t_quad.colours.colour[2].a);    glTexCoord2f(t_quad.texture_coordinates.coordinate[2].x, t_quad.texture_coordinates.coordinate[2].y);    glVertex2f(t_quad.coordinates.x + t_quad.coordinates.w, t_quad.coordinates.y + t_quad.coordinates.h);    glColor4f(t_quad.colours.colour[3].r, t_quad.colours.colour[3].g, t_quad.colours.colour[3].b, t_quad.colours.colour[3].a);    glTexCoord2f(t_quad.texture_coordinates.coordinate[3].x, t_quad.texture_coordinates.coordinate[3].y);    glVertex2f(t_quad.coordinates.x, t_quad.coordinates.y + t_quad.coordinates.h);}else{    if (enable_texture)    {   glEnd();   glDisable(GL_TEXTURE_2D);   glBegin(GL_QUADS);   enable_texture = false;    }    glColor4f(t_quad.colours.colour[0].r, t_quad.colours.colour[0].g, t_quad.colours.colour[0].b, t_quad.colours.colour[0].a);    glVertex2f(t_quad.coordinates.x, t_quad.coordinates.y);    glColor4f(t_quad.colours.colour[1].r, t_quad.colours.colour[1].g, t_quad.colours.colour[1].b, t_quad.colours.colour[1].a);    glVertex2f(t_quad.coordinates.x + t_quad.coordinates.w, t_quad.coordinates.y);    glColor4f(t_quad.colours.colour[2].r, t_quad.colours.colour[2].g, t_quad.colours.colour[2].b, t_quad.colours.colour[2].a);    glVertex2f(t_quad.coordinates.x + t_quad.coordinates.w, t_quad.coordinates.y + t_quad.coordinates.h);    glColor4f(t_quad.colours.colour[3].r, t_quad.colours.colour[3].g, t_quad.colours.colour[3].b, t_quad.colours.colour[3].a);    glVertex2f(t_quad.coordinates.x, t_quad.coordinates.y + t_quad.coordinates.h);}quads.pop(); } glEnd(); if (enable_texture) {glDisable(GL_TEXTURE_2D); }  }Thanks for taking the time :)   ;  Unfortunately I lack the time (but surely had enough time to find out that we could only guess;)). But have you already tried valgrind (included in most linux distros, some graphical frontends available (I use KCachegrind)) to find the exact location in code where you lack performance?   ;Quote:Original post by phresnelUnfortunately I lack the time (but surely had enough time to find out that we could only guess;)). But have you already tried valgrind (included in most linux distros, some graphical frontends available (I use KCachegrind)) to find the exact location in code where you lack performance?Yes, it wasn't really that I wanted you or someone else to find my lack of performance...I thought there could perhaps be some common-knowledge about unnecessary-initializing-penalty or something along those lines.Thanks for your time though.Unfortunately I cannot run valgrind here in Windows7. I yet have to setup a dual boot with Ubuntu or Debian or something...I will do that soon than...:)   ;  One thing worth noting is that because gprof adds some profiling code to each function, it will overestimate the relative cost of small functions. The simplest way to see that is to force the compiler to inline some small functions and then reprofile. For both tests look at the total time spent inside the parent functions.If you can find a sampling profiler that should give more balanced results. Since you're running Windows you could try Very Sleepy assuming you can get gcc to create a .pdb file.Also when you say release build with profiling, what flags did you pass to the compiler?   ;Quote:Original post by Adam_42One thing worth noting is that because gprof adds some profiling code to each function, it will overestimate the relative cost of small functions. The simplest way to see that is to force the compiler to inline some small functions and then reprofile. For both tests look at the total time spent inside the parent functions.If you can find a sampling profiler that should give more balanced results. Since you're running Windows you could try Very Sleepy assuming you can get gcc to create a .pdb file.Also when you say release build with profiling, what flags did you pass to the compiler?Thanks for mentioning Very Sleepy, I forgot about that one ;), though the .pdb file will be a bit of a problem I think...For Release build (I think I need to profile code that is actually going to run -> Release build), I use the following flags:-pg (obviously)-O-O1-O2-O3-fexpensive-optimizations-fno-function-sections[Edited by - Decrius on April 20, 2010 11:03:03 AM];Quote:Listed as most-time-consuming function was First things first - is this a problem? 17% of 1ms is just that - no time. That is something to consider - there will *always* be a hotspot, even if that hotspot takes 26 CPU cycles. But something will always be the slowest.---------------STL containers are sub optimal for this type of use. The by far simplest solution is to use them inplace, rather than pushing and popping.Something like:std::vector<Quad> quads(MAX);...quads[top].set(coordinates, colors); // or whatever needs to be settop++;Quote:quads.pop();This is also redundant. Iterate over all, then clear the queue with clear().Quote: i_target->quad(Rectangle(i_coordinate.x + i_character.x * m_resize, i_coordinate.y + (-i_character.y) * m_resize, i_character.w * m_resize, i_character.h * m_resize),m_texture_handle,TextureCoordinates(    Coordinate(i_character.tx, i_character.ty),    Coordinate(i_character.tw, i_character.ty),    Coordinate(i_character.tw, i_character.th),    Coordinate(i_character.tx, i_character.th)) );Too many intermediates.Since there are so many parameters being passed into function call, the Coordinates likely need to be properly created in stack.Quote:dynamic_castCalled once per quad rendered. Definitely not needed.Quote:current_texture != t_quad.texture_handle->id()Instead of queue, use a vector. Sort it by texture. It may not matter much right now, but it's going at some point.There's simply way too much indirection in the code. Usually one would simply make an array or vector of all rectangles, then manipulate them in place. Then all of the above is reduced to simple for loop with no copying at all. Especially since VBOs will need to be used as soon as number of quads goes into thousands.   ;  Thank you Antheus. I understand mostly what you say, its good to have someone else look in depth in the code.Quote:Original post by AntheusToo many intermediates.Since there are so many parameters being passed into function call, the Coordinates likely need to be properly created in stack.Are you referring to the whole function call, or mostly to how TextureCoordinates has Coordinates nested? Coordinates(float, float) was indeed not far from the top with gprof.Quote:Original post by AntheusQuote:dynamic_castCalled once per quad rendered. Definitely not needed.What do you propose? Should I just (static) cast it then?Quote:Original post by AntheusThere's simply way too much indirection in the code. Usually one would simply make an array or vector of all rectangles, then manipulate them in place. Then all of the above is reduced to simple for loop with no copying at all. Especially since VBOs will need to be used as soon as number of quads goes into thousands.Yes, good point. I am very much planning to implement DLs, VAs and VBOs, as well as render-to-texture caching. This is the first bit so I do the, naive, IM approach first.Thanks for your advice, if only I could get someone as crazy as to look at the whole lib, hehe ;)EDIT:Quote:Original post by AntheusQuote:Listed as most-time-consuming function was First things first - is this a problem? 17% of 1ms is just that - no time. That is something to consider - there will *always* be a hotspot, even if that hotspot takes 26 CPU cycles. But something will always be the slowest.Hehe, from 0.33 ms actually (per frame). Though it still bothers me as it should be code that should be the fastest. The font code should've been the slowest...   