2012-05-10 6 views
9

Ich mache ein Spiel mit XNA-Framework, also verwende ich viele Funktionen, die auf Vektoren arbeiten. (insbesondere Vector2 (64-Bit-Struktur)). Was mich stört ist, dass die meisten Methoden mit Ref und Out Parametern definiert sind. Hier ein Beispiel:Welchen Nutzen hat die Verwendung von out/ref im Gegensatz zu return?

void Min(ref Vector2 value1, ref Vector2 value2, out Vector2 result) 

das sieht ein bisschen komisch aus mir auch. Es gibt auch eine andere Min die

public static Vector2 Min(Vector2 value1, Vector2 value2); 

Grundsätzlich offensichtlicher ist, fast alle Funktionen haben Überlastungen mit ref s und out s. Ähnlich, andere APIs.

Was ist der Vorteil dieses Designs? XNA ist für die Leistung optimiert, könnte es ein Ergebnis sein? Sagen wir, Quaternion benötigt 128b, wenn man weniger an ref vorbeifährt.

EDIT:

Hier ist ein Testcode:

public class Game1 : Microsoft.Xna.Framework.Game 
{ 
    GraphicsDeviceManager graphics; 
    SpriteBatch spriteBatch; 

    private Vector2 vec1 = new Vector2(1, 2); 
    private Vector2 vec2 = new Vector2(2, 3); 
    private Vector2 min; 
    private string timeRefOut1; 
    private string timeRefOut2; 
    private SpriteFont font; 

    public Game1() 
    { 
     graphics = new GraphicsDeviceManager(this); 
     Content.RootDirectory = "Content"; 

     refOut1(); 
     refOut2(); 
    } 

    private Vector2 refOut1() 
    { 
     Vector2 min = Vector2.Min(vec1, vec2); 
     return min; 
    } 

    private Vector2 refOut2() 
    { 
     Vector2.Min(ref vec1, ref vec2, out min); 
     return min; 
    } 

    protected override void Initialize() 
    { 
     const int len = 100000000; 
     Stopwatch stopWatch = new Stopwatch(); 
     stopWatch.Start(); 
     for (int i = 0; i < len; i++) 
     { 
      refOut1(); 
     } 
     stopWatch.Stop(); 

     timeRefOut1 = stopWatch.ElapsedMilliseconds.ToString(); 

     stopWatch.Reset(); 
     stopWatch.Start(); 
     for (int i = 0; i < len; i++) 
     { 
      refOut2(); 
     } 
     stopWatch.Stop(); 

     timeRefOut2 = stopWatch.ElapsedMilliseconds.ToString(); 

     base.Initialize(); 
    } 

    protected override void LoadContent() 
    { 
     spriteBatch = new SpriteBatch(GraphicsDevice); 
     font = Content.Load<SpriteFont>("SpriteFont1"); 
    } 

    protected override void Update(GameTime gameTime) 
    { 
     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) 
      this.Exit(); 

     base.Update(gameTime); 
    } 

    protected override void Draw(GameTime gameTime) 
    { 
     GraphicsDevice.Clear(Color.CornflowerBlue); 

     spriteBatch.Begin(); 
     spriteBatch.DrawString(font, timeRefOut1, new Vector2(200, 200), Color.White); 
     spriteBatch.DrawString(font, timeRefOut2, new Vector2(200, 300), Color.White); 
     spriteBatch.End(); 

     // TODO: Add your drawing code here 

     base.Draw(gameTime); 
    } 
} 

Die Ergebnisse:

  • refOut1 2200
  • refOut2 1400

Win 7 64bit. Net 4. XNA 4.0

Auch IL-Code

.method public hidebysig static void Min(valuetype Microsoft.Xna.Framework.Vector2& value1, 
              valuetype Microsoft.Xna.Framework.Vector2& value2, 
              [out] valuetype Microsoft.Xna.Framework.Vector2& result) cil managed 
{ 
    // Code size  69 (0x45) 
    .maxstack 3 
    IL_0000: ldarg.2 
    IL_0001: ldarg.0 
    IL_0002: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0007: ldarg.1 
    IL_0008: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_000d: blt.s  IL_0017 
    IL_000f: ldarg.1 
    IL_0010: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0015: br.s  IL_001d 
    IL_0017: ldarg.0 
    IL_0018: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_001d: stfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0022: ldarg.2 
    IL_0023: ldarg.0 
    IL_0024: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0029: ldarg.1 
    IL_002a: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_002f: blt.s  IL_0039 
    IL_0031: ldarg.1 
    IL_0032: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0037: br.s  IL_003f 
    IL_0039: ldarg.0 
    IL_003a: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_003f: stfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0044: ret 
} // end of method Vector2::Min 

und

.method public hidebysig static valuetype Microsoft.Xna.Framework.Vector2 
     Min(valuetype Microsoft.Xna.Framework.Vector2 value1, 
      valuetype Microsoft.Xna.Framework.Vector2 value2) cil managed 
{ 
    // Code size  80 (0x50) 
    .maxstack 3 
    .locals init (valuetype Microsoft.Xna.Framework.Vector2 V_0) 
    IL_0000: ldloca.s V_0 
    IL_0002: ldarga.s value1 
    IL_0004: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0009: ldarga.s value2 
    IL_000b: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0010: blt.s  IL_001b 
    IL_0012: ldarga.s value2 
    IL_0014: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0019: br.s  IL_0022 
    IL_001b: ldarga.s value1 
    IL_001d: ldfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0022: stfld  float32 Microsoft.Xna.Framework.Vector2::X 
    IL_0027: ldloca.s V_0 
    IL_0029: ldarga.s value1 
    IL_002b: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0030: ldarga.s value2 
    IL_0032: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0037: blt.s  IL_0042 
    IL_0039: ldarga.s value2 
    IL_003b: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0040: br.s  IL_0049 
    IL_0042: ldarga.s value1 
    IL_0044: ldfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_0049: stfld  float32 Microsoft.Xna.Framework.Vector2::Y 
    IL_004e: ldloc.0 
    IL_004f: ret 
} // end of method Vector2::Min 

scheint Overhead durch Temp Vector verursacht wird. Auch habe ich versucht, 1GHz WP 7.5 Gerät:

Anzahl der Ticks für eine Größenordnung kleinere Anzahl von Iterationen.

+1

Ich denke, die * echte * Frage ist, "sollte .NET nicht RVO tun?" – Mehrdad

Antwort

7

Vector2 ist eine Struktur, was bedeutet, dass bei Rückgabe als Wert eine Kopie zurückgegeben wird, anstatt einen Verweis auf eine vorhandene Struktur zurückzugeben. Wenn Sie Ref/Out-Parameter verwenden, können Sie diese Kopie vermeiden, sodass der Vektor, der in der Min-Methode erstellt wird, der genaue Vektor in Ihrer result-Variable ist.

Es ist eine jener Mikro-Optimierung, die normalerweise entmutigt werden würde, aber in der Spielwelt ist es oft genug, und in Umgebungen, in denen Leistung wichtig genug ist, ist es die etwas weniger lesbare Option wert.

+0

Das Zuweisen eines Werttyps zu einem Parameter out/ref "kopiert" auch den Wert von der lokalen Variablen in den Parameter. –

+2

@PeterRitchie Der ganze Punkt wäre, dass der Parameter out/ref als lokale Variable in der gesamten Methode verwendet würde, so dass Sie nicht die Kopie von der lokalen Variablen zum Parameter out/ref haben. Andernfalls, ja, würden Sie eine zusätzliche Kopie durchführen und es würde den Zweck vereiteln. – Servy

+1

Sie weisen immer noch einen Wert von einer Variablen zur anderen zu (der Parameter). Die Zuweisung ist eine "Kopier" -Operation; Solange Sie einer Variablen (unabhängig vom Typ) einen Werttyp zuweisen, "kopieren" Sie den Wert. –

3

Ein weiterer Unterschied zu der von Servy erwähnten Leistungseffizienz ist die Möglichkeit, mehrere "Rückgabe" -Werte zu haben: Anstatt sie auf die übliche Weise zurückzugeben, listen Sie sie als ref/var-Parameter auf.

+2

Das, was mein erster Gedanke, aber sie geben nur 1 Wert zurück. –

+0

@lukas - in diesem Fall, ja, ich schrieb für den allgemeinen Fall :) – Attila

Verwandte Themen