使用 out/ref 与返回相比有什么好处? [英] What is the benefit of using out/ref versus returning?
问题描述
我正在使用 XNA 框架制作游戏,所以我使用了很多对向量进行操作的函数.(尤其是 Vector2(64 位结构)).困扰我的是大多数方法都是用 ref 和 out 参数定义的.下面是一个例子:
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:
void Min(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
我也觉得有点奇怪.还有一个Min
更明显
which looks a bit strange too me. There is also another Min
which is more obvious
public static Vector2 Min(Vector2 value1, Vector2 value2);
基本上,几乎所有的函数都有带有 ref
s 和 out
s 的重载.类似的,其他API.
Basically, almost all the functions have overloads with ref
s and out
s. Similar, other APIs.
这种设计有什么好处? XNA 针对性能进行了优化,这可能是结果吗?说,四元数需要 128b,其中通过 ref 更少.
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.
这是一个测试代码:
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);
}
}
结果:
- refOut1 2200
- refOut2 1400
Win 7 64 位,.Net 4. XNA 4.0
Win 7 64bit, .Net 4. XNA 4.0
还有 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
和
.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
似乎开销是由临时向量引起的.我还尝试了 1GHz WP 7.5 设备:
Seems overhead is caused by temp Vector. Also I tried 1GHz WP 7.5 device:
- 1979
- 1677
迭代次数少一个数量级的滴答数.
Number of ticks for an order of magnitude smaller number of iterations.
推荐答案
Vector2 是一个结构体,这意味着当它作为值返回时,将返回一个副本,而不是返回对现有结构的引用.通过使用 ref/out 参数,您可以避免这种复制,以便在 Min 方法中创建的 Vector 是 result
变量中的确切向量.
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.
这是通常不鼓励的微优化之一,但在游戏世界中它已经足够频繁了,并且在性能足够重要的环境中,值得稍微降低可读性的选项.
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.
这篇关于使用 out/ref 与返回相比有什么好处?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!