Pytanie w sprawie .net, xna, c#, api – Jaka jest korzyść z używania out / ref versus return?

9

Tworzę gry z wykorzystaniem struktury XNA, więc używam wielu funkcji, które działają na wektorach. (w szczególnościVector2 (Struktura 64-bitowa)). Martwi mnie to, że większość metod jest definiowana z parametrami ref i out. Oto przykład:

<code>void Min(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
</code>

co też wydaje mi się trochę dziwne. Jest też innyMin co jest bardziej oczywiste

<code>public static Vector2 Min(Vector2 value1, Vector2 value2);
</code>

Zasadniczo prawie wszystkie funkcje mają przeciążeniarefs iouts. Podobne, innePszczoła.

Jakie są zalety tego projektu? XNA jest zoptymalizowany pod kątem wydajności, czy to może wynik? Powiedzmy, że kwaternion wymaga 128b, gdzie przechodzi przez ref mniej.

EDYTOWAĆ:

Oto kod testowy:

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

Wyniki:

refOut1 2200refOut2 1400

Win 7 64bit, .Net 4. XNA 4.0

Również kod 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
</code>

i

<code>.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
</code>

Wydaje się, że narzut jest spowodowany przez temp Vector. Spróbowałem również urządzenia 1 GHz WP 7.5:

19791677

Liczba znaczników dla rzędu wielkości mniejszej liczby iteracji.

Myślęreal pytanie brzmi: „nie powinno .NET robić RVO?” Mehrdad

Twoja odpowiedź

2   odpowiedź
3

Servy jest możliwość posiadania wielu wartości „powrotu”: zamiast zwracać je w zwykły sposób, podaje się je jako parametry ref / var.

@lukas - w tym przypadku tak, pisałem w ogólnym przypadku :) Attila
To właśnie moja pierwsza myśl, ale zwracają tylko 1 wartość. Lukasz Madon
7

co oznacza, że ​​gdy zostanie zwrócona jako wartość, zwracana jest kopia, zamiast zwracać odwołanie do istniejącej struktury. Używając parametrów ref / out, możesz uniknąć tej kopii, dzięki czemu wektor utworzony w metodzie Min jest dokładnym wektoremresult zmienna.

Jest to jedna z tych mikro optymalizacji, która normalnie byłaby zniechęcająca, ale w świecie gry często się to robi, aw środowiskach, w których wydajność ma znaczenie, jest warta nieco mniej czytelnej opcji.

Dzięki! Wydaje się, że korzyść jest uzasadniona. 10% do prawie 50% przyspieszenia. Lukasz Madon
Przypisanie typu wartości do parametru out / ref również „kopiuje” wartość ze zmiennej lokalnej do parametru. Peter Ritchie
@PeterRitchie Cały punkt byłby taki, że parametr out / ref byłby użyty jako zmienna lokalna w całej metodzie, tak aby nie było kopii zmiennej lokalnej na parametr out / ref. W przeciwnym razie tak, wykonałbyś dodatkową kopię i to by zniweczyło cel. Servy
Nadal przypisujesz wartość z jednej zmiennej do drugiej (parametr). Przypisanie jest operacją „kopiowania”; tak długo, jak przypisujesz typ wartości zmiennej (niezależnie od typu), „kopiujesz” wartość. Peter Ritchie
@PeterRitchie Vector2 to zmienna struktura. Możesz je nowy i przypisać doresult (Trywialna operacja dla struktury), a następnie zmodyfikuj ją w oparciu o logikę metody. Servy

Powiązane pytania