2012-05-10 2 views
9

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

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

, который выглядит немного странно, меня тоже. Существует также еще один Min что более очевидно

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

В принципе, почти все функции имеют перегруженные с ref с и out с. Подобный, другой APIs.

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

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

Вот код теста:

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 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 устройства:

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

+1

Я думаю, что * реальный вопрос: «не должен ли .NET делать RVO?» – Mehrdad

ответ

7

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

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

+0

Назначение типа значения параметру out/ref также «копирует» значение из локальной переменной в параметр. –

+2

@PeterRitchie Весь смысл в том, что параметр out/ref будет использоваться как локальная переменная во всем методе, так что у вас нет копии из локальной переменной в параметр out/ref. В противном случае, да, вы бы выполнили дополнительную копию, и это победит цель. – Servy

+1

Вы по-прежнему назначаете значение из одной переменной в другую (параметр). Назначение - операция «копирования»; до тех пор, пока вы назначаете тип значения переменной (независимо от типа), вы «копируете» значение. –

3

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

+2

Это моя первая мысль, но они возвращают только 1 значение. –

+0

@ lukas - в этом случае, да, я писал для общего случая :) – Attila

Смежные вопросы