Overload operator [] should start at 1 and overhead

I do some computational mechanics in C ++ (don't worry, no physical knowledge is required here), and there is something that really bothers me.

Suppose I want to represent a 3D mathematical vector (do nothing with std :: vector):

class Vector { public: Vector(double x=0., double y=0., double z=0.) { coordinates[0] = x; coordinates[1] = y; coordinates[2] = z; } private: double coordinates[3]; }; 

So far so good. Now I can overload operator [] to retrieve the coordinates:

 double& Vector::operator[](int i) { return coordinates[i] ; } 

Therefore, I can print:

 Vector V; … //complex computation with V double x1 = V[0]; V[1] = coord2; 

The problem is that indexing from 0 is not natural here. I mean, when sorting arrays, I don’t mind, but the fact is that the conditional notation in each article, book or even always substitutes the coordinates starting with 1. This may seem like a cripple, but the thing is that in the formulas a double approach is always required to understand what we are doing. Of course, this is much worse with matrices.

One obvious solution is to just slightly overload:

 double& Vector::operator[](int i) { return coordinates[i-1] ; } 

so i can print

 double x1 = V[1]; V[2] = coord2; 

It seems perfect, with one exception: this is an i-1 subtraction, which seems like a good candidate for small overheads. There is very little that you would say, but I deal with computational mechanics, so this is usually something we could not afford.

So now (finally) my question is: do you think that the compiler can optimize this, or is there a way to make it optimized? (templates, macro, pointer or kludge link ...)

Logically, in

 double xi = V[i]; 

an integer between the bracket that is literal most of the time (except for 3 iterations for loops), the inlining [] operator should make this possible, right?

(sorry for this question)

EDIT:

Thanks for your comments and answers.

I disagree with what people tell me that we are used to 0-indexed vectors. From an object-oriented point of view, I see no reason for a mathematical vector to be indexed 0, because it is implemented with a 0-indexed array. We do not have to worry about the underlying implementation. Now suppose I don't care about performance and use a map to implement the Vector class. Then it would be natural for me to display "1" with the coordinate "1st".

This says that I tried with 1-indexed vectors and matrices, and after writing some code, I find that it doesn't interact nicely every time I use an array around. I have a Vector and containers (std :: array, std :: vector ...) will not often interact (which means transferring data between each other), but it seems like I was wrong.

Now I have a solution that, in my opinion, is less controversial (please give me your opinion): Every time I use Vector in some kind of physical context, I think about using an enumeration:

 enum Coord { x = 0, y = 1, z = 2 }; Vector V; V[x] = 1; 

The only drawback that I see is that these x, y and z can be overridden without warning ...

+6
source share
6 answers

This needs to be measured or verified by looking at the disassembly, but I assume that the getter function is tiny and its arguments are constant. There is a high probability that the compiler will inline the function and hide the subtraction. In this case, the execution cost will be zero.

+11
source

Why not try this:

 class Vector { public: Vector(double x=0., double y=0., double z=0.) { coordinates[1] = x; coordinates[2] = y; coordinates[3] = z; } private: double coordinates[4]; }; 

If you are not creating an instance of the object in the amount of millions, then the waist of the memory may be available.

+3
source

Did you really profile it or study the generated code? This is the answer to this question.

If the operator [] implementation is visible, then it will probably be optimized to have zero overhead.

+1
source

I recommend that you define this in the header (.h) for your class. If you define it in .cpp, then the compiler cannot optimize as much. Also, your index should not be "int", which may have negative values ​​... make it size_t:

 class Vector { // ... public: double& operator[](const size_t i) { return coordinates[i-1] ; } }; 
+1
source

You cannot say anything objective about performance without benchmarking. On x86, this subtraction can be compiled using relative addressing, which is very cheap. If operator[] is built-in, then the overhead is zero β€” you can recommend this with inline or with instructions for the compiler, such as GCC __attribute__((always_inline)) .

If you have to guarantee this, and the offset is a compile-time constant, then using a template is the way to go:

 template<size_t I> double& Vector::get() { return coordinates[i - 1]; } double x = v.get<1>(); 

For all practical purposes, a zero overhead flow rate is guaranteed due to continuous folding. You can also use named accessors:

 double Vector::x() const { return coordinates[0]; } double Vector::y() const { return coordinates[1]; } double Vector::z() const { return coordinates[2]; } double& Vector::x() { return coordinates[0]; } double& Vector::y() { return coordinates[1]; } double& Vector::z() { return coordinates[2]; } 

And for loops, iterators:

 const double* Vector::begin() const { return coordinates; } const double* Vector::end() const { return coordinates + 3; } double* Vector::begin() { return coordinates; } double* Vector::end() { return coordinates + 3; } // (x, y, z) -> (x + 1, y + 1, z + 1) for (auto& i : v) ++i; 

Like many others, I do not agree with the premise of your question. You really should just use 0-based indexing, as it is more natural in C ++. The language is already very complicated, and you do not need to complicate the situation for those who will support your code in the future.

+1
source

Seriously, compare these three methods (that is, compare the subtraction methods and double [4] using only zero indices in the caller).

It is possible that you will get a huge victory from forcing 16-byte alignment on some cache architectures, and the ability to subtract is effectively freed from some compiler / instruction set / code combinations.

The only way to tell is to check realistic code.

0
source

Source: https://habr.com/ru/post/922324/


All Articles