That is the thing with micro-benchmarks: It can make a huge difference (up to 2x times faster) using ref, but there are hardly any use-cases when it matters.
For example the Vector.VectorTests.TestNormalize tests calling "vector = Vector.Normalize(vector)" vs "Vector.Normalize(ref vector)" and it almost makes no difference because the normalize code eats up all the CPU cycles plus the way we have to call it negates any benefit. The difference is greater when calling Vector.Normalize with Vector.Zero because the method will early out:
Code:
TestNormalize : Passed (Release Mode)
149ms for Vector.Normalize (10 million calls)
148ms for Static Vector.Normalize (10 million calls)
143ms for Vector.Normalize Zero (10 million calls)
82ms for Static Vector.Normalize Zero (10 million calls)
The results get even stranger when the Vector.Normalize methods (static or instance version) call another framework. All the extra overhead makes them twice as slow, which is why we rarely call XnaVector.Normalize, etc. The Build System has also a much easier time to optimize just one way of calling code than supporting both static and instance call optimizations. For example physics, geometry and other low level platform calls can be optimized by exchanging Vector calls to whatever the library or framework uses (XNA vectors, SlimDX, SharpDX, etc.) OpenTK datatypes are never used because they seem to be always slower.
But you are totally right to check and double-check and see if anything is slow and should use ref instead. That is why I said before feel free to modify the Delta.Utilities.Datatypes classes and commit your fork on CodePlex so others can benefit as well and we might merge it if it makes sense. In your code you might use a lot more math code, which could need more optimization than a typical game, so it might be more important for you to optimize than for others. Keep in mind we also tried to keep the number of methods low in all high level classes (mostly less than 10 methods you see when Intellisense opens, so it fits in the initial suggestions), which is obviously not so easy with a struct like Vector, which has to provide lots of functionality.
Still, we tried to keep the number of methods low and avoided duplicates in v0.7 (the ZombieParty version). However each game team was annoyed to be forced to use ref calls, so we added the slower non-ref calls again, which caused several game teams to use really slow code. Then in v0.8.3 (after the Soulcraft Tech Demo) we added Obsolete attribute warnings for all calls that are bad, so people could optimize their code. It still did only annoy people and they turned the warnings of or removed the Obsolete attributes ^^ So we finally settled in v0.8.6 (last internal beta release) to remove the Obsolete warnings and just write comments in the Xml summary of the slow methods (like Matrix.operator *). Plus we are also able to catch some unoptimized code in the build system and replace it with faster code, which seems to be more productive in the long run. Usually it does not make a game or application much better when the developer has to worry about which method to call every time he just wants to multiply some matrices or calculate some rotations, etc. I certainly don't think much about it anymore. Performance issues are usually much higher level (geometry, material optimizations, shaders, etc.)
Here is the source code for Vector.VectorTests.TestNormalize (changed it a bit for testing):
Code:
[Test]
public static void TestNormalize()
{
Vector testVector1 = new Vector(10, 20, 30);
// Results (Release)
// -------
// 2010-04-08
// Delta: 150 ms
// XNA: 350 ms
// OpenTK: 450 ms
// SlimDx: 330 ms
Vector vector = One;
PerformanceTester.Profile10MilionTimes("Vector.Normalize",
delegate
{
vector = Normalize(testVector1);
});
Assert.Equal(vector, Normalize(testVector1));
// Do the same test with a static call
PerformanceTester.Profile10MilionTimes("Static Vector.Normalize",
delegate
{
vector = One;
Vector.Normalize(ref vector);
});
Assert.Equal(vector, Normalize(One));
// And finally test it again with Zero, which returns faster
vector = Zero;
PerformanceTester.Profile10MilionTimes("Vector.Normalize Zero",
delegate
{
vector = Normalize(testVector1);
});
Assert.Equal(vector, Normalize(testVector1));
// And finally test it again with Zero, which returns faster
PerformanceTester.Profile10MilionTimes("Static Vector.Normalize Zero",
delegate
{
Vector zeroVector = Zero;
Vector.Normalize(ref zeroVector);
});
}