10
\$\begingroup\$

UPDATE: This question refers to Farseer 2.x. The newer 3.x doesn't seem to do this.

I'm using Farseer Physics Engine quite extensively at the moment, and I've noticed that it seems to store a lot of temporary value types as members of the class, and not on the stack as one might expect.

Here is an example from the Body class:

private Vector2 _worldPositionTemp = Vector2.Zero;

private Matrix _bodyMatrixTemp = Matrix.Identity;
private Matrix _rotationMatrixTemp = Matrix.Identity;
private Matrix _translationMatrixTemp = Matrix.Identity;

public void GetBodyMatrix(out Matrix bodyMatrix)
{
    Matrix.CreateTranslation(position.X, position.Y, 0, out _translationMatrixTemp);
    Matrix.CreateRotationZ(rotation, out _rotationMatrixTemp);
    Matrix.Multiply(ref _rotationMatrixTemp, ref _translationMatrixTemp, out bodyMatrix);
}

public Vector2 GetWorldPosition(Vector2 localPosition)
{
    GetBodyMatrix(out _bodyMatrixTemp);
    Vector2.Transform(ref localPosition, ref _bodyMatrixTemp, out _worldPositionTemp);
    return _worldPositionTemp;
}

It looks like its a by-hand performance optimisation. But I don't see how this could possibly help performance? (If anything I think it would hurt by making objects much larger).

\$\endgroup\$

3 Answers 3

6
\$\begingroup\$

Although in .NET value types are stored on the stack, resulting in a minimal allocation cost, it does not however eliminate the cost of initialization.

In this case we have a set of functions using one or two temporary matrices, which would result in the initialization of 16-32 floats per call. While this may seem insignificant, if the methods are used often enough (say, thousands and thousands of times per frame), the total overhead can have a meaningful impact. If such a technique is used systematically across all such methods, the overhead eliminated can be considerable.

While use of such a technique eliminates the ability to provide thread safety at a per-object level, it is generally unwise to provide that guarantee at such a granular level.

\$\endgroup\$
6
  • \$\begingroup\$ Are you sure about that? One thought I had was that it might be to avoid calling constructors. But for value types you don't need to call a constructor - including the default parameterless constructor - if you're going to set all the members (or pass it as an out parameter). I'm fairly sure the entire point of this rule is so the compiler can skip zeroing that memory - right? (Is it really that slow to move the stack pointer?) \$\endgroup\$ Jul 18, 2010 at 6:29
  • \$\begingroup\$ Surprising, no? Unfortunately, if you inspect the generated IL, the temporary matrices are initialized. A few quick tests show the member-temp version is ~10-15% faster. \$\endgroup\$ Jul 18, 2010 at 14:43
  • 1
    \$\begingroup\$ I am stunned. In "Understanding XNA Framework Performance" (GDC2008) Shawn Hargreaves says of structs: "[the JIT] will usually figure out: 'in the next line he immediately sets all three fields [of the Vector3], so I don't even need to initialize it to zero'". Which is where my info is coming from. But on listening again now, he only says "usually". The immediate next point in the presentation is that the JIT behaves differently with the debugger attached, affecting performance (how did you test?). Also: he is talking about the JIT here, so perhaps the IL stays "nice" (verifiability?). \$\endgroup\$ Jul 18, 2010 at 15:14
  • \$\begingroup\$ The IL was inspected via Reflector, and the tests were run outside the IDE built in Release (on Windows, I no longer have a CC membership to test against) \$\endgroup\$ Jul 18, 2010 at 15:29
  • 1
    \$\begingroup\$ Based on this - I wonder if it would be better (and how much better it would be) to make those member-temporaries static (and/or more aggressively reuse them). As it is, just for example, the Body class in Farseer has some 73 floats worth of "unnecessary" members. \$\endgroup\$ Jul 19, 2010 at 10:02
-1
\$\begingroup\$

Good question. I'm a pretty sharp C#/.NET guy and a bit of a performance nut, and this seems like a rather odd design decision to me. The first thing that jumps out at me is that this code is in no way thread safe. I don't know if that's an issue in a Physics system, but storing temporary data outside the scope of a method is often a recipe for disaster.

Honestly, if I regularly encountered this kind of code in a third-party framework, I would probably try to find another framework.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ Doesn't really answer the question. \$\endgroup\$ Jul 16, 2010 at 19:29
  • \$\begingroup\$ Yeah, the best I could do is confirm that he's not crazy, and there doesn't seem to be any real benefit it being coded that way. The only find out the true intent is to ask the guy who wrote the code :). \$\endgroup\$ Jul 17, 2010 at 1:27
  • \$\begingroup\$ Thanks, Mike. I'm beginning to suspect that the original developer is the crazy one, not me. But it always helps to check ;) \$\endgroup\$ Jul 17, 2010 at 2:34
  • \$\begingroup\$ Thread safety can sometimes be a costly guarantee to provide, especially when writing a FP computation heavy library for a platform that doesn't make use of SIMD instructions. \$\endgroup\$ Jul 17, 2010 at 15:13
-1
\$\begingroup\$

The GC on the 360 basically only does GEN 2 collections, which are expensive, so temporary variables that get created and removed every frame (like temp objects) cause a whole collections to run, which will kill performance really fast.

I suspect they did it this way to reuse that object and not have it collected.

\$\endgroup\$
9
  • 1
    \$\begingroup\$ This occurred to me as well, but the temporary members appear to be value types, so they wouldn't be allocated on the managed heap anyway. \$\endgroup\$ Jul 16, 2010 at 16:15
  • 2
    \$\begingroup\$ Right, but only if they are class members. If they are locals (method-scoped), they would be allocated on the stack. The question is why they they didn't simply go that route. \$\endgroup\$ Jul 16, 2010 at 19:22
  • 1
    \$\begingroup\$ @Blair - According to MSDN (msdn.microsoft.com/en-us/library/bb203912.aspx) the Xbox360 uses the .NET Compact Framework. It seems that the difference in GC is related to that, so I'd pursue that for further research on the matter. \$\endgroup\$ Jul 17, 2010 at 3:04
  • 1
    \$\begingroup\$ @Blair: @Logan Kincaid is correct. The CF's Garbage Collector behaves differently than the regular framework's. There's a good talk on the subject in XNA Game Studio 3.0 Unleashed - however that book will soon be outdated with the release of 4.0. \$\endgroup\$ Jul 17, 2010 at 5:17
  • 1
    \$\begingroup\$ @Blair: Due to the limited memory environment of the 360 (and most devices targeted via the CF), a Mark & Sweep GC is used. As a result, lots of small allocations will trigger a collection, and collection time is relative to the # of references. Lots of details here: download.microsoft.com/.../Mobility/… \$\endgroup\$ Jul 17, 2010 at 15:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .