使用out / ref与return相比有什么好处?

时间:2022-11-30 22:37:49

I'm making a game using XNA framework, so I use a lot functions that operate on vectors. (especially Vector2 (64bit struct)). What bothers me is that most of the methods are defined with ref and out parameters. Here is an example:

我正在使用XNA框架制作一个游戏,所以我使用很多在向量上运行的函数。 (特别是Vector2(64位结构))。困扰我的是大多数方法都是用ref和out参数定义的。这是一个例子:

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

which looks a bit strange too me. There is also another Min which is more obvious

我也觉得有点奇怪。还有另一个明显更明显

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

Basically, almost all the functions have overloads with refs and outs. Similar, other APIs.

基本上,几乎所有的功能都有refs和outs的重载。类似的,其他API。

What is the benefit of this design? XNA is optimized for performance, could it be a result? Say, Quaternion requires 128b where passing by ref less.

这个设计有什么好处? XNA针对性能进行了优化,可能是结果吗?说,Quaternion需要128b,其中ref更少。

EDIT:

编辑:

Here is a test 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);
    }
}

The results:

结果:

  • refOut1 2200
  • refOut1 2200
  • refOut2 1400
  • refOut2 1400

Win 7 64bit, .Net 4. XNA 4.0

赢7 64位,.Net 4. XNA 4.0

Also IL code

IL代码

.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

and

.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

Seems overhead is caused by temp Vector. Also I tried 1GHz WP 7.5 device:

似乎开销是由temp Vector引起的。我也试过1GHz WP 7.5设备:

  • 1979
  • 1979年
  • 1677
  • 1677

Number of ticks for an order of magnitude smaller number of iterations.

一个数量级的迭代次数的刻度数。

2 个解决方案

#1


7  

Vector2 is a struct, which means that when it's returned as a value a copy is returned, rather than returning a reference to an existing structure. By using ref/out parameters you can avoid this copy so that the Vector created in the Min method is the exact vector in your result variable.

Vector2是一个结构体,这意味着当它作为值返回时,将返回一个副本,而不是返回对现有结构的引用。通过使用ref / out参数,您可以避免此副本,以便在Min方法中创建的Vector是结果变量中的精确向量。

It's one of those micro optimization that normally would be discouraged, but in the game world it's done often enough, and in environments where performance matters enough, that it's worth the slightly less readable option.

这是通常不鼓励的那些微优化之一,但在游戏世界中它经常做得足够,并且在性能足够重要的环境中,值得稍微不那么可读的选项。

#2


3  

Another difference on top of the performance efficience mentioned by Servy is the ability to have multiple "return" values: instead of returning them the usual way, you list them as ref/var parameters.

Servy提到的性能效率之外的另一个区别是能够拥有多个“返回”值:而不是按常规方式返回它们,将它们列为ref / var参数。

#1


7  

Vector2 is a struct, which means that when it's returned as a value a copy is returned, rather than returning a reference to an existing structure. By using ref/out parameters you can avoid this copy so that the Vector created in the Min method is the exact vector in your result variable.

Vector2是一个结构体,这意味着当它作为值返回时,将返回一个副本,而不是返回对现有结构的引用。通过使用ref / out参数,您可以避免此副本,以便在Min方法中创建的Vector是结果变量中的精确向量。

It's one of those micro optimization that normally would be discouraged, but in the game world it's done often enough, and in environments where performance matters enough, that it's worth the slightly less readable option.

这是通常不鼓励的那些微优化之一,但在游戏世界中它经常做得足够,并且在性能足够重要的环境中,值得稍微不那么可读的选项。

#2


3  

Another difference on top of the performance efficience mentioned by Servy is the ability to have multiple "return" values: instead of returning them the usual way, you list them as ref/var parameters.

Servy提到的性能效率之外的另一个区别是能够拥有多个“返回”值:而不是按常规方式返回它们,将它们列为ref / var参数。