2012-05-10 34 views
9

Estoy haciendo un juego usando XNA framework, entonces uso muchas funciones que operan en vectores. (especialmente Vector2 (estructura de 64 bits)). Lo que me molesta es que la mayoría de los métodos se definen con los parámetros ref y out. Aquí está un ejemplo:¿Cuál es el beneficio de usar out/ref versus returning?

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

que se parece un poco extraño demasiado mí. También hay otro Min que es más evidente

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

Básicamente, casi todas las funciones tienen sobrecargas con ref s y out s. Similar, otro APIs.

¿Cuál es el beneficio de este diseño? XNA está optimizado para el rendimiento, ¿podría ser un resultado? Digamos que Quaternion requiere 128b cuando pasa por ref menos.

EDIT:

Aquí hay un código de prueba:

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); 
    } 
} 

Los resultados:

  • refOut1 2200
  • refOut2 1400

Win 7 64 bits,. Net 4. XNA 4.0

También IL código

.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 

y

.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 

Parece sobrecarga es causada por Vector temp. También probé 1GHz WP 7.5 dispositivo:

número de garrapatas de un orden de magnitud más pequeño número de iteraciones.

+1

Supongo que la pregunta * real * es "¿no debería .NET hacer RVO?" – Mehrdad

Respuesta

7

Vector2 es una estructura, lo que significa que cuando se devuelve como valor se devuelve una copia, en lugar de devolver una referencia a una estructura existente. Al usar los parámetros de ref/out puede evitar esta copia para que el Vector creado en el método Min sea el vector exacto en su variable result.

Es una de esas micro optimizaciones que normalmente se desaconsejarían, pero en el mundo del juego se hace con la suficiente frecuencia, y en entornos donde el rendimiento importa lo suficiente, vale la pena la opción un poco menos legible.

+0

La asignación de un tipo de valor a un parámetro out/ref también está "copiando" el valor de la variable local al parámetro. –

+2

@PeterRitchie Todo el punto sería que el parámetro out/ref se usaría como la variable local en todo el método para que no tenga la copia de la variable local al parámetro out/ref. De lo contrario, sí, estarías realizando una copia adicional y eso vencería el propósito. – Servy

+1

Aún está asignando un valor de una variable a otra (el parámetro). La asignación es una operación de "copia"; siempre que asigne un tipo de valor a una variable (independientemente del tipo), está "copiando" el valor. –

3

Otra diferencia además de la eficiencia de rendimiento mencionada por Servy es la capacidad de tener múltiples valores de "devolución": en lugar de devolverlos de la forma habitual, los enumera como parámetros ref/var.

+2

Eso fue lo primero que pensé, pero solo devuelven 1 valor. –

+0

@lukas - en este caso, sí, estaba escribiendo para el caso general :) – Attila

Cuestiones relacionadas