tags:

views:

260

answers:

13

Hi all

I dont know which is the best practice when we want to create a new vector 3D class, i mean, which of this two examples is the best way ?

class Vec3D
{
   private:

         float m_fX;
         float m_fY;
         float m_fZ;

...
};

or

class Vec3D
{
   private:

         float m_vVec[3];
...
};

With the first aproach, we have individual variables, we canot be sure to be contiguous in memory, so caches can fail, but access to this variables are a single instruction.

With the second aproach, we have a vector of 3 contiguous floats in memory, caches are fine here, but every access will make an extra sum of variable offset. Buti think that this vector aproach could fit better with optimitzations like SSE2/3 or something.

Which aproach is better, i'm lost, i need advices :)

Thanks for your time :)

LLORENS

+1  A: 

Option 3?

struct Vec3D
{
    // ...
}
Jon Seigel
+2  A: 

I'd prefer the first approach because it will make your code more readable.

Kirill V. Lyadvinsky
+2  A: 

I would go with the former one, mainly because of readability. It would be very easy to get lost in an equation if you have lots of array indexes, but if you have it explicitly as x,y,z then it is easier to understand what is going on.

James Black
+2  A: 

Both forms are identical in memory layout, unless your compiler has some very weird padding going on. Member variables will be placed together if they are declared together in the class.

I'd make the choice based on the clarity of the code which will be using the variables.

Mark Ransom
What's the importance of memory layout? Using the first version and accessing the elements via pointer arithmetic invokes undefined behaviour, strictly speaking. I'd go with the second version, provide begin() and end() functions and maybe some inline x() y() z() member functions as well.
sellibitze
The original question asked about memory layout with respect to cache effects. I wasn't in any way condoning pointer arithmetic. The compiler should generate identical instructions for p->m_fY vs. p->m_vVec[1].
Mark Ransom
A: 

From a performance point of view, the compiler should be able to emit equally efficient code for both. However, there are pros and cons to both.

The first is definitely more readable. However, the second one lets you grab a variable by index. So if you have a need to do something like add all 2 vectors together, that code would be simpler:

for(int i = 0; i < 3; ++i) {
    vVec[i] += o.vVec[i];
}

vs.

m_fX += o.m_fX;
m_fY += o.m_fY;
m_fZ += o.m_fZ;

So I would say, do whichever you find more comfortable and readable based on what you are trying to achieve.

Evan Teran
What does the second for loop aim to achieve exactly? ;)
Ates Goral
oops :-P good point.
Evan Teran
It looks like a copy paste induced bug.
Burkhard
+10  A: 

use

class Vec3D
{
   private:
    union 
    {
        float m_vVec[3];
        struct
        {
             float m_fX;
             float m_fY;
             float m_fZ;
        };
    };
    ...
}

this will give you both at no extra cost

slashmais
Spot on. This is exactly how I represented a 3D vector when I wrote it in C++.
FreeMemory
While I use this method, be aware that anonymous structs are a compiler extension so make sure you have support on your platform.
Ron Warholic
VMMlib is written in exactly this way for ease of use with OpenGL. http://vmmlib.sourceforge.net/
greyfade
Thank you for your comments, i will take care of your advices :)
Llorens Marti
A: 

I would definitely go for the second approach. In many places of your code, it will allow you to have an elegant loop instead of a long repetition. Consider this:

for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    VecMult.m_vVec[i] += Vec1.m_vVec[i] * Vec2.m_vVec[j];
}

Versus this:

  VecMult.m_fX = Vec1.m_fX * Vec2.m_fX + Vec1.m_fX * Vec2.m_fY + Vec1.m_fX * Vec2.m_fZ;
  VecMult.m_fY = Vec2.m_fX * Vec2.m_fX + Vec2.m_fX * Vec2.m_fY + Vec2.m_fX * Vec2.m_fZ;
  VecMult.m_fZ = Vec3.m_fX * Vec2.m_fX + Vec3.m_fX * Vec2.m_fY + Vec3.m_fX * Vec2.m_fZ;
Igor Oks
Hmm, I would take the first approach, for exactly the same reason - if I cared about performance, which usually is the case with vectors.
Andy J Buchanan
+1  A: 

Assuming you're going to be doing computationally intensive stuff with your vectors, I would suggest making your members public (or simply use a struct instead of a class). You should skip the overhead of getters and just access the vector members directly.

In terms of syntax, the first form is more readable. If you'll ever need to access the members as an array of 3 values, you could also consider using a union that gives you both individual member and array access.

Ates Goral
I'm not sure there is any overhead with inline getters and setters. However, a Vec3D just holds 3 values and couldn't care less what they are. Therefore I don't see the point of going through the effort to hide them. For example see std::pair<T, U>. What would it buy you, other than more verbous syntax, if first and second were private? (If a particular Vec3 or pair is significant to a user, it's the user that hides that particular instance from the outside world.)
UncleBens
A: 

Both have pros and cons. Usually v1.X * v2.Y is more readable then v1[0] * v2[1]. But there are algorithms that index the vector and the second solutions allows to write v1[a] * v2[b]. In this case the first solution will call for ugly if or switch blocks. I am not a C++ programmer, but maybe you could get the best out of two by using macros or whatever C++ supports (a union as suggested by others seems a good candidate). In C# i would create something like the following using properties.

public class Vector
{
    private readonly Single[] components = new Single[3];

    public Single X
    {
        get { return components[0]; }
        set { this.components[0] = value; }
    }

    public Single Y { ... }
    public Single Z { ... }
}

Finally I believe performance will be no issue, because the indexing can be handled by the address calculation units of the processor.

Daniel Brückner
+4  A: 

The second one will make matrix operations much simpler.

Before you write your own vector and matrix classes, you might want to take a look through the code for something like openscengraph

Martin Beckett
+1  A: 

The more important question you should ask yourself is would you ever need to refer to the coordinates by indices, or would calling them x, y, and z suffice.

Dima
A: 

I am against implementing your own 3D vector class.

Although such a class seems to be straightforward enough, it requires an incredible amount of work to make an efficient, reliable, robust and accurate class of this kind.

There is nothing worse than spending many hours hunting down a strange bug, only to eventually find that it is caused by numerical instability in your vector class producing the wrong answer for some particular input. Believe me, I have been there!

There are many libraries available, tried and tested over many years by thousands of users so they can be used with confidence.

A library I have used successfully for many years is Andrew Willmott’s Simple Vector Library. http://www.cs.cmu.edu/~ajw/doc/svl.html

SVL is simple and straightforward. However, it does have a very old-fashioned API and, for me, the problem that it is yet another third-party library which needs to be linked in and loaded by my clients.

So, recently, I have been using boost::uBLAS and in particular the fixed size vector wrapper based on the one described here: http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Effective_UBLAS

The boost library is, as always, intimidating when you first approach it. However, it is superbly efficient and complete, actively maintained and comes for “free” when boost is being linked into your program anyway.

ravenspoint
A: 

I would use the second (array) approach for the simple fact that it allows you to use STL algorithms to implement member functions.

For example:

#include <cmath>
#include <numeric>

float Vec3D::Magnitude()
{
   return std::sqrt(std::inner_product(m_vVec, m_vVec + 3, m_vVec, 0.0f));
}
mocj