Вопрос по c#, .net, api – В чем преимущество использования аут / реф по сравнению с возвратом?

9

Я создаю игру, используя платформу XNA, поэтому я использую множество функций, которые работают с векторами. (особенно Vector2 (64-битная структура)). Что меня беспокоит, так это то, что большинство методов определены с параметрами ref и out. Вот пример:

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

что тоже немного странно выглядит для меня. Есть и еще одинMin что более очевидно

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

В основном, почти все функции имеют перегрузки сrefs иouts. Похоже, другое APIs.

В чем преимущество этого дизайна? XNA оптимизирован для производительности, это может быть результатом? Скажем, Quaternion требует 128b, где мимо ref меньше.

РЕДАКТИРОВАТЬ

Вот тестовый код:

<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>

Результаты

refOut1 2200 refOut2 1400

Win 7 64bit, .Net 4. XNA 4.0

Также 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>

а такж

<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>

акладные расходы @Seems вызваны временным вектором. Также я попробовал 1GHz WP 7.5 устройство:

1979 1677

Количество тиков на порядок меньшее количество итераций.

Я думаю, что Реального вопрос в том, "не должен ли .NET делать RVO?" Mehrdad

Ваш Ответ

2   ответа
3

упомянутой Servy, является возможность иметь несколько «возвращаемых» значений: вместо того, чтобы возвращать их обычным способом, вы перечисляете их как параметры ref / var.

Я так и думал, но они возвращают только 1 значение. Lukasz Madon
@ lukas - в этом случае, да, я писал для общего случая:) Attila
7

которая означает, что когда она возвращается в качестве значения, возвращается копия, а не ссылка на существующую структуру. Используя параметры ref / out, вы можете избежать этой копии, чтобы вектор, созданный в методе Min, был точным вектором в вашем result переменная.

Это одна из тех микрооптимизаций, которые обычно не поощряются, но в игровом мире это делается достаточно часто, а в средах, где производительность важна, стоит немного менее читабельный вариант.

@ PeterRitchie Весь смысл в том, что параметр out / ref будет использоваться в качестве локальной переменной во всем методе, чтобы у вас не было копии из локальной переменной в параметр out / ref. В противном случае, да, вы выполняете дополнительную копию, и это побьет цель. Servy
Присвоение типа значения параметру out / ref также означает «копирование» значения из локальной переменной в параметр. Peter Ritchie
Вы по-прежнему присваиваете значение из одной переменной в другую (параметр). Назначение является операцией «копирования»; пока вы назначаете тип значения переменной (независимо от типа), вы «копируете» значение. Peter Ritchie
@ PeterRitchie Vector2 - изменчивая структура. Вы можете создать новый и присвоить егоresult (Тривиальная операция для структуры), а затем измените ее в зависимости от логики метода. Servy
Благодарность! Кажется, что выгода оправдана. От 10% до почти 50% ускорения. Lukasz Madon

Похожие вопросы