views:

259

answers:

7

I am creating a geometry library in C# and I will need the following immutable types:

  • Vector2f (2 floats - 8 bytes)
  • Vector2d (2 doubles - 16 bytes)
  • Vector3f (3 floats - 12 bytes)
  • Vector3d (3 doubles - 24 bytes)
  • Vector4f (4 floats - 16 bytes)
  • Vector4d (4 doubles - 32 bytes)

I am trying to determine whether to make them structs or classes. MSDN suggests only using a struct if the size if going to be no greater than 16 bytes. That reference seems to be from 2005. Is 16 bytes still the max suggested size?

I am sure that using structs for the float vectors would be more efficient than using a class, but what should I do about the double vectors? Should I make them structs also to be consistent, or should I make them classes?

Updated: Looks like the everyone agrees they should be structs. Thanks for the great answers.

A: 

I assume you're worried about perf?

If they're immutable, they should be behaviorally identical. Just go with one, and if it turns out that there's a perf issue, switch it later on.

If you're targetting .NET CE at all, you should probably consider using structs as the GC isn't as efficient as the full .NET implementation.

kyoryu
It can be tough later on to switch -- he's creating a geometry library, and simply switching the fundamental types will break all of the consumers of his library. Better to make the decision early and correctly.
Dave Markle
"Correct" may be determined by issues that he doesn't have enough data to fully know yet. At any rate, it is likely that his library will do some internal work and testing before it is released. I would hope that he'd have enough info to make a good decision pre-release.
kyoryu
+4  A: 

I would generally make types like these structs. Structs are more likely to be placed on the stack, and if you use arrays with them, you can get much nicer peformance than if, say, you were to use a List<> of objects. As long as you stay away from operations which will cause your vector classes to be boxed, the struct is the generally going to be the higher performance way to go.

Dave Markle
A: 

Structs are (usually) stored on the stack, which might make them more efficient, but probably not enough to make a noticeable difference.

http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory01122006130034PM/csharp_memory.aspx?ArticleID=9adb0e3c-b3f6-40b5-98b5-413b6d348b91

Dave Swersky
No, structs are not (always) stored on the stack.
Henk Holterman
And the linked article at least gets that much right, although it mistakenly claims that all variables of value type declared inside a method are stored on that stack. Actually, a declaration inside a method can create either a local variable (stored on the stack) or a closure (stored on the heap).
Ben Voigt
@Henk, @Ben: So the upvoted and accepted answer says essentially the same thing as mine and @kyoryu's answers... which get downvoted?
Dave Swersky
Dave, not my -1 but saying "Structs are stored on the stack" is a sure way of getting them here. Read up on the subject.
Henk Holterman
+7  A: 

Microsoft's XNA Framework uses structures for its Vector2/3/4 data types. These contain fields of type float. I don't see anything wrong with doing the same; using fields of type double shouldn't make a big difference.

dtb
WPF also uses structs for its Vector/Rect/Points, which contain double fields instead of floats (and this includes up to 4 fields in the case of Point4D)
jeffora
+2  A: 

In my opinion I would go with structs for this, since they would be allocated on the stack most times this would reduce the pressure on the GC so your application should run smoother.

One tip, your methods that opperate on the structs should probably take the arguments as ref and out arguments, this will reduce the amount of copying of the data that takes place when passing and returning structs.

Chris Taylor
The _ref_ and _out_ 'tip' is (in general) bad advice
Henk Holterman
@Henk: I don't understand why you discourage `ref` and `out`. After all, classes (reference types) have nearly the same behavior implicitly. A struct-passed-by-ref is just as efficient as a class, without straining the GC.
Ben Voigt
@Ben - one possible reason is that you lose the whole "size" advantage, especially on x64. If the type is small and immutable it *might* be faster just to copy it.
Marc Gravell
Ben (and @Marc), ref and out should be used when the situation calls for it, not as an optimization. Your 'tip' is suggesting to do so as a matter of course, I would only use it very (very) rarely, and add a ton of comments to it.
Henk Holterman
@Henk: I admit the wording could be more explicit, but the tip is specifically to this situation where this is going to be used in a 3d library. Especially with the definition of types like Matrix which are inevitable in a lib of this nature.
Chris Taylor
+2  A: 

We recently switched some maths types (vec3, vec4, matrices etc.) over to use structs instead of classes and, as well as being slightly quicker in benchmarks, it also reduced the GC pressure (we had hundreds of megabytes of Vec3 allocations over a relatively short period of time, discovered via the CLR profiler).

It's interesting to note that the Xna implementation of Vectors & Matrices is done using structs. You can always pass by reference where appropriate if you're concerned about performance, too.

Mark Simpson
+1: Nice example. How big were your matrix structs?
dewald
They range from a 2x2 of floats to a 4x4 of floats. We don't really use anything bigger, so although I'm confident you'll get good results with floats, I'm not sure if doubles will prove to be a bridge too far. Profile and find out, I guess!
Mark Simpson
+2  A: 

Structs, definitely. Why? Well, it's part feeling I guess, a vector just feels and behaves like a value.

Rico Mariani here gives some reasons why value types were chosen for certain types for an API (I don't think it's XNA he's talking about).

And while efficiency is a factor here, I don't think it's about garbage collection, but more about data density like Rico says. Say you also have a Vertex type, that contains two Vector3s: a Vector3 for the normal and a Vector3 for the world co-ordinates. If you made those types classes, then having an array with 100 Vertex elements, would consist of:

  • 100 * 8 bytes (8 bytes is I believe the overhead of a class in memory, 4 bytes for the type header and 4 bytes for something else, a GC handle?)
  • 100 * 4 bytes (for the pointers in the array to the Vertex elements)
  • 200 * 4 bytes (for the pointers from each Vertex to the two Vector3 elements)
  • 200 * 8 bytes (for the 8 byte overhead that you pay for making Vector3 a class)
  • 200 * 12 bytes (for the actual payload of 3 float per Vector3)

6000 bytes (on a 32-bit system).

As a value type, it's simply 200 * 12 bytes = 2400 bytes. So much more efficient space-wise not to mention a lower level of indirection when reading the array.

But taking up a lot of space doesn't necessarily make it slow, using a value type incorrectly can be slower than making it a class, as I have found out. You definitely want to pass them by ref as much as possible, avoid copying them, but this doesn't go for all operations, so measure. I think I remember calculating the dot-product was slower when passing by ref, perhaps because it somehow prevented in-lining by making the IL larger. But don't take my word for it, just measure.

JulianR
+1 for mentinoning the essential reason for going with structs: the value semantics of a vector (for this kind of R^n-vector)
Novox