fkj's picture

Vector3.Mult() - Obsolete - Use static Multiply() method instead

I'm new to OpenTK, and having to multiply the length of a vector I found the Mult()-method. This method is marked obsolete, and a comment refers to the static Multiply()-method. Why is the instance method obsolete?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Having both instance and static methods would result in ridiculous amounts of code duplication. Static methods are superior because they preserve immutability, so they are way forward. (XNA uses static methods for the same reason).

fkj's picture

The static methods create instances of Vector3, they could create them using a copy-constructor all the same, and then call the instance-method to perform the operation, without code duplication.

Why are static methods superior because they preserve immutability? I would not consider implementing the static methods.

the Fiddler's picture

Duplication in that you provide two different ways to perform the same operation. This is not good API design. Besides, the amount of typing for code and documentation involved is way too much for the benefit (just take a look at the source code).

fkj's picture

There are actually two operations. A mutable operation where you want to change a vector by multiplying it with a scale, and an immutable operation where you want to get a new vector being the result of multiplying a vector with a scale.

Currently Vector3 has three implementations of this: one mutable (the instance-method, marked obsolete), and two immutable (the static method and the operator *).

I will suggest that the obsolete-remark is removed from the instance method, and placed on the static method, making it refer to the operator *. The static method is procedural and hence obsolete (unless it is really needed) and the operator * makes it redundant. If I want to perform thousands of mutable vector multiplications I don't want the memory manager having to allocate thousands of vectors I don't need, and the only way I can avoid this is by using the obsolete instance method (which I do).

Additionally I will suggest to overload the operator *, to implement the cross product:

public static Vector3 operator *( Vector3 a, Vector3 b ) ...

the Fiddler's picture
Quote:

The static method is procedural and hence obsolete (unless it is really needed) and the operator * makes it redundant.

Operator * is slower than Vector3.Multiply(ref, ref, out), which is why both are necessary.

Quote:

If I want to perform thousands of mutable vector multiplications I don't want the memory manager having to allocate thousands of vectors I don't need, and the only way I can avoid this is by using the obsolete instance method (which I do).

Vectors, matrices and quaternions are value types, precisely to avoid unnecessary GC pressure. The instance method does not offer any advantage here.

(Insert obligatory comment on premature optimization and the use of profilers).

fkj's picture

If the operator * is slower (why is it slower?), then why are both required? Why not leave the operator * out, using your argument against an API having two ways of doing the same thing, and the maintainability issue?

About structs, I very rarely use them, but I see your point on the GC issue.

The instance methods being marked obsolete, and the static methods being refered to as the prefered way of doing things, gives the impression that at the start of OpenTK someone (A) had the idea to make it object-orientered, but then someone (B) came along arguing about efficience. If this is the case, A had the right idea and B really argued against making OpenTK in the first place. If OpenTK is not willing to make the paradigme shift from procedural to object-oriented it makes little sence to make it! I'm NOT saying the procedural paradigme is bad, I'm saying if you want to program within the procedural paradigme choose a procedural language - it's called C not C#.

the Fiddler's picture
fkj wrote:

If the operator * is slower (why is it slower?), then why are both required? Why not leave the operator * out, using your argument against an API having two ways of doing the same thing, and the maintainability issue?

Operator * pushes its arguments to the stack and copies the result back, exactly like static Vector3 Multiply(Vector3, Vector3). The ref overload passes its parameters in registers, which brings a measurable performance improvement.

On the other hand, operators are much cleaner to use, which is why both are available (this is what most/all .net math libraries do).

Quote:

The instance methods being marked obsolete, and the static methods being refered to as the prefered way of doing things, gives the impression that at the start of OpenTK someone (A) had the idea to make it object-orientered, but then someone (B) came along arguing about efficience. If this is the case, A had the right idea and B really argued against making OpenTK in the first place. If OpenTK is not willing to make the paradigme shift from procedural to object-oriented it makes little sence to make it! I'm NOT saying the procedural paradigme is bad, I'm saying if you want to program within the procedural paradigme choose a procedural language - it's called C not C#.

Initially, we had both instance and static methods. This soon became a maintenance burden so we decided to drop half of them. XNA came out around that time and it used static methods - we decided to do the same. (This is ancient history).

Also note that OpenTK.Graphics.OpenGL is 100% procedural, so it's not as if OpenTK is some bastion of OO. :)

fkj's picture

@Fiddler — Thank you for all your replies :)

Dave's picture
the Fiddler wrote:
fkj wrote:

If the operator * is slower (why is it slower?), then why are both required? Why not leave the operator * out, using your argument against an API having two ways of doing the same thing, and the maintainability issue?

Operator * pushes its arguments to the stack and copies the result back, exactly like static Vector3 Multiply(Vector3, Vector3). The ref overload passes its parameters in registers, which brings a measurable performance improvement.

Really? Which register would that be?
Did you actually try to measure it? I found no significant differences (edit actually by value wins) - which makes me think its optimised to the same code.

Use of the stack and simple memory copies tend to be very fast because reference fetches are not as cachable.
You'd typically only want to pass by value for larger objects than 3d vectors.

A reference: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/