Page 1 of 1
Vector3 member initialization
Posted: Thu Nov 25, 2010 1:37 am
by Peet
Personally, when I call the default constructor of a class, I make the assumption that that class' members are now initialized to a sane non-NaN value. I believe this places me in same category as the general programming public - it strikes me a pretty reasonable assumption. Similarly, the Ogre coding guidelines state
All class variables should be initialized to a sane value in the constructor.
So why is the Vector3 class a blatant exception to this? People in #ogre3d have been making the argument that leaving initialization to the users of the library has a performance advantage, but the constructor is inline which means any resultant multiple assignments will be optimized out by any sane compiler. Even if that were a valid argument, the complete lack of documentation of this fact makes it a very notable pitfall for developers using Ogre.
Re: Vector3 member initialization
Posted: Thu Nov 25, 2010 3:09 am
by dark_sylinc
Peet wrote:People in #ogre3d have been making the argument that leaving initialization to the users of the library has a performance advantage, but the constructor is inline which means any resultant multiple assignments will be optimized out by any sane compiler.
No.
Anything where the initialization is obfuscated, may not get inlined and initialization can't be optimized out.
Consider this simple DLL example:
Code: Select all
__declspec(dllexport) Ogre::Vector3 returnMyVector() { return Ogre::Vector3(); }
When this function gets called from an exe to the dll, the compiler has no way to know at compile time what happens to the returned values or what is being returned. There's little chance of optimization.
Furthermore, floating point optimizations are much less aggressive than one would think, compared to integer optimizations or any other kind (which is so, in order to preserve precision). Even obvious optimizations like a = a / 2.0f may not be optimized to a = a * 0.5f because in floating point, this isn't actually the same.
Peet wrote:Even if that were a valid argument, the complete lack of documentation of this fact makes it a very notable pitfall for developers using Ogre.
Fair point. Advanced users of 3D graphic engines assume that things like Vectors and Quaternions aren't usually initialized (the latter
is initialized in Ogre); so we forget programmers new into 3D programming don't know this.
Re: Vector3 member initialization
Posted: Thu Nov 25, 2010 6:49 am
by Peet
dark_sylinc wrote:Peet wrote:People in #ogre3d have been making the argument that leaving initialization to the users of the library has a performance advantage, but the constructor is inline which means any resultant multiple assignments will be optimized out by any sane compiler.
No.
Anything where the initialization is obfuscated, may not get inlined and initialization can't be optimized out.
Consider this simple DLL example:
Code: Select all
__declspec(dllexport) Ogre::Vector3 returnMyVector() { return Ogre::Vector3(); }
When this function gets called from an exe to the dll, the compiler has no way to know at compile time what happens to the returned values or what is being returned. There's little chance of optimization.
This is a bit of a ridiculous argument...If the constructor of Vector3 is called inside the ogre so/dll from the game (tip: it's not), 3 additional assignments are not the primary optimization concern here
dark_sylinc wrote:Furthermore, floating point optimizations are much less aggressive than one would think, compared to integer optimizations or any other kind (which is so, in order to preserve precision). Even obvious optimizations like a = a / 2.0f may not be optimized to a = a * 0.5f because in floating point, this isn't actually the same.
How is this even relevant? Only assignment is in question here; mentioning other operations is just a red herring....
dark_sylinc wrote:Advanced users of 3D graphic engines assume that things like Vectors and Quaternions aren't usually initialized (the latter is initialized in Ogre); so we forget programmers new into 3D programming don't know this.
Telling me that a similar class DOES initialize its members doesn't exactly strengthen your justification for not initializing a vector in its default ctor. This sort of inconsistency doesn't only affect "programmers new into 3D programming", and I would definitely appreciate it if you would not assume that someone whose views do not match your own is inexperienced.
Re: Vector3 member initialization
Posted: Thu Nov 25, 2010 9:14 am
by jacmoe
The difference is probably that Vector3 is meant to be treated like a primitive type.
Just like int, float, etc.
Re: Vector3 member initialization
Posted: Thu Nov 25, 2010 9:24 am
by so0os
Does float get initialised whgen you declare it? What's the point?!?! if you need a vector, i think you want to use it, and if you do you get a warning.
Re: Vector3 member initialization
Posted: Thu Nov 25, 2010 3:44 pm
by Peet
jacmoe wrote:The difference is probably that Vector3 is meant to be treated like a primitive type.
Just like int, float, etc.
This is sensible; all I ask is that it be noted in the doxygen documentation.
so0os wrote:Does float get initialised whgen you declare it? What's the point?!?! if you need a vector, i think you want to use it, and if you do you get a warning.
Vector3 is a class, not a built in type. Arguing implicitly that it is to be treated like a primitive type is not valid (and there is no warning that a Vector3 is uninitialized since it just calls the empty default constructor).
Re: Vector3 member initialization
Posted: Thu Nov 25, 2010 6:12 pm
by dark_sylinc
Peet wrote:This is a bit of a ridiculous argument...If the constructor of Vector3 is called inside the ogre so/dll from the game (tip: it's not), 3 additional assignments are not the primary optimization concern here

It was an example. It extends to any DLL function that returns Vector3, any Vector3 initialized in the exe but passed to the DLL, or any function that accepts a reference or pointer as an argument.
By the way, it's not just "3 additional assignments". It's 3 additional assignments per Vector3 declaration (there are too many) per frame. And half or more of them won't be optimized away sadly.
Peet wrote:How is this even relevant? Only assignment is in question here; mentioning other operations is just a red herring....
Again, an example. If the compiler can't be guaranteed the value is uninitialized, it has to shift some bits of instruction in some situations to adjust to this. It holds more important when passing a (const or not) reference, than when passing by copy.
Peet wrote:This sort of inconsistency doesn't only affect "programmers new into 3D programming", and I would definitely appreciate it if you would not assume that someone whose views do not match your own is inexperienced.
I chose carefully my words when phrasing it, so that it would be clear that I didn't say "inexperience programmer". Even the most experienced of programmers can't know what are the common uses and traditions in a certain field, this case 3D graphics; until he gets fond with it.
jacmoe & so0os are right in that the intention is to treat Vector2/Vector3/Vector4 as a data type, maintaining consistency with GPU architectures (shaders); where vectorN is a native data type.
But you're right the documentation could and should be more explicit about this. And certainly doesn't hurt adding it.
By the way there's no default "correct" initialization of a Vector3, because it depends on the intention. When you're working with scales, the "most valid" initialization is Ogre::Vector3::UNIT_SCALE, when you're working with rotations/quaternions the valid initialization is either UNIT_X/UNIT_Y/UNIT_Z or their negative counterparts.
Forcing these explicit initializations helps visualizing the intention and usage of math code. And these initializations are probably more common and used throughout the whole engine than setting everything to zeros.
Cheers
Dark Sylinc
Re: Vector3 member initialization
Posted: Sat Nov 27, 2010 9:31 pm
by sparkprime
It's not actually that uncommon to have a class type that is not fully initialised upon construction. Not preferable, either, of course. I think the style guides need updating and better documentation. Personally I figured it out by reading the source code, which is what I always do when the documentation is unclear.
Re: Vector3 member initialization
Posted: Sun Nov 28, 2010 10:39 am
by kneeride
Hi guys, this topic has come up a number of times. I couldn't find the post but sinbad once said that that this decision was based on performance. Somewhere in the code is a an array of vectors and removing the initialisation improved performance significantly (I assume by avoiding construction on multiple objects). Sinbad had played around with both ideas and decided on handling vectors as a primative type for this reason. Sorry but I couldnt find the post.
Re: Vector3 member initialization
Posted: Sun Nov 28, 2010 11:41 am
by jbb
Also, the compiler is often able to warn you that you are reading an uninitialised variable - and tools like purify certainly will.
If you initialise it to anything else, it's almost certainly not the correct value, but you've lost the ability of your tools to warn you about it.
Re: Vector3 member initialization
Posted: Sun Nov 28, 2010 4:43 pm
by dark_sylinc
kneeride wrote:Hi guys, this topic has come up a number of times. I couldn't find the post but sinbad once said that that this decision was based on performance. Somewhere in the code is a an array of vectors and removing the initialisation improved performance significantly (I assume by avoiding construction on multiple objects). Sinbad had played around with both ideas and decided on handling vectors as a primative type for this reason. Sorry but I couldnt find the post.
Ahhh yes. I'm not familiar with the original thread, but it gives
one of the many examples involving trivial STL operation, in which code can't be optimized by a compiler:
Code: Select all
std::vector<Ogre::Vector3> myArray;
myArray.resize( 1000 );
The compiler can't tell if you, for any chance will later be doing this:
Code: Select all
Ogre::Vector3 *myPtr = &myArray[123];
float x = myPtr->x; //reading uninitialized variable or...
myPtr->x = 120.0f; //...setting variables to valid values, skipping the default ones
Of course something more sane is to initialize with a loop all 1000 variables. The thing is, the compiler
can't know what the heck you will be doing with the heap memory or even with the STL container if it reaches a higher scope (i.e. return value). As a result the compiler can't assume and will always (or at least 99% of cases) initialize those 1.000 vectors.
The resize is just one of many examples. Copy operations often involve constructors, which I'm guessing would be probably the biggest cause of performance slowdowns.
Furthermore Ogre has been expanding to handhelds lately, and compilers for those platforms aren't nearly as advanced as x86 compilers when it comes to optimizations.
My thoughts those guidelines applies mostly to true objects (MaterialManager, Material, Mesh, SubMesh, SceneManager) which by the way aren't initialized that often, and they should have a sane value. But Vector3 acts more as a container of floats, than rather an object IMHO.