使用ref而不是返回相同类型的性能成本?

use*_*057 13 c# performance ref

嗨这是真的困扰我的事情,我希望有人能给我一个答案.我一直在阅读ref(和out),我正在试图弄清楚我是否正在使用refs 减慢我的代码.通常我会替换类似的东西:

int AddToInt(int original, int add){ return original+add; }
Run Code Online (Sandbox Code Playgroud)

void AddToInt(ref int original, int add){ original+=add; } // 1st parameter gets the result
Run Code Online (Sandbox Code Playgroud)

因为我的眼睛这个

AddToInt(ref _value, _add);
Run Code Online (Sandbox Code Playgroud)

比这更容易阅读和编码

_value = AddToInt(_value, _add);
Run Code Online (Sandbox Code Playgroud)

我确切地知道我在使用代码时正在做什么ref,而不是返回一个值.但是,性能是我认真对待的,显然,当你使用refs时,解除引用和清理会慢得多.

我想知道的是为什么我读的每个帖子都说你通常会传递的地方很少ref(我知道这些例子是人为的,但我希望你能得到这个想法),当我觉得这个ref例子是更小,更清洁,更准确.

我也很想知道为什么ref真的比返回一个值类型慢 - 对我来说,在我看来,如果我要在返回它之前编辑很多函数值,那么引用实际变量会更快在从内存中清除之前不久编辑它而不是该变量的实例.

Mar*_*ell 12

"ref"在与性能相同的发送中使用的主要时间是在讨论一些非常典型的情况时,例如在XNA场景中,游戏"对象"通常由结构而不是类来表示以避免GC的问题(其中对XNA产生不成比例的影响).这对以下内容很有用:

  • 防止在堆栈上多次复制超大结构
  • 防止因修改struct副本而导致数据丢失(XNA结构通常是可变的,与正常做法相反)
  • 允许直接在数组中传递结构,而不是将其复制出来并重新插入

在所有其他情况下,"ref"通常与附加的副作用相关联,不易在返回值中表示(例如,请参阅Monitor.TryEnter).

如果你没有像XNA/struct那样的场景,并且没有尴尬的副作用,那么只需使用返回值.除了更典型(其本身具有价值)之外,它可能涉及传递更少的数据(例如,int小于x64上的ref),并且可能需要更少的解除引用.

最后,返回方法更通用; 你不想要更新源代码.对比:

// want to accumulate, no ref
x = Add(x, 5);

// want to accumulate, ref
Add(ref x, 5);

// no accumulate, no ref
y = Add(x, 5);

// no accumulate, ref
y = x;
Add(ref y, x);
Run Code Online (Sandbox Code Playgroud)

我认为最后一个是最不明确的(其他"ref"紧随其后)并且ref 语言在不明确的语言中更为明显(例如VB).


dra*_*112 6

使用ref关键字的主要目的是表示变量的值可以通过传递给它的函数来改变.按值传递变量时,函数内的更新不会影响原始副本.

对于需要多个返回值并为返回值构建特殊结构或类的情况,它非常有用(并且更快),这将是过度的.例如,

public void Quaternion.GetRollPitchYaw(ref double roll, ref double pitch, ref double yaw){
    roll = something;
    pitch = something;
    yaw = something;
}
Run Code Online (Sandbox Code Playgroud)

对于不受限制地使用指针的语言来说,这是一个非常基本的模式.在c/c ++中,您经常会看到基元以类和数组作为指针传递的基元.C#恰恰相反,所以'ref'在上述情况下很方便.

当您通过ref将要更新的变量传递给函数时,只需要1次写操作即可为您提供结果.但是,当返回值时,通常会写入函数内的某个变量,返回它,然后再将其写入目标变量.根据数据,这可能会增加不必要的开销.无论如何,这些是我在使用ref关键字之前通常考虑的主要事项.

有时在c#中这样使用ref会快一点,但是不足以将它用作性能的goto理由.

这是我在一台7岁机器上使用下面的代码传递并通过ref和value更新100k字符串的内容.

输出:

迭代次数:10000000 byref:165ms byval:417ms

private void m_btnTest_Click(object sender, EventArgs e) {

    Stopwatch sw = new Stopwatch();

    string s = "";
    string value = new string ('x', 100000);    // 100k string
    int iterations = 10000000;

    //-----------------------------------------------------
    // Update by ref
    //-----------------------------------------------------
    sw.Start();
    for (var n = 0; n < iterations; n++) {
        SetStringValue(ref s, ref value);
    }
    sw.Stop();
    long proc1 = sw.ElapsedMilliseconds;

    sw.Reset();

    //-----------------------------------------------------
    // Update by value
    //-----------------------------------------------------
    sw.Start();
    for (var n = 0; n < iterations; n++) {
        s = SetStringValue(s, value);
    }
    sw.Stop();
    long proc2 = sw.ElapsedMilliseconds;

    //-----------------------------------------------------
    Console.WriteLine("iterations: {0} \nbyref: {1}ms \nbyval: {2}ms", iterations, proc1, proc2);
}

public string SetStringValue(string input, string value) {
    input = value;
    return input;
}

public void SetStringValue(ref string input, ref string value) {
    input = value;
}
Run Code Online (Sandbox Code Playgroud)


Jas*_*ans 5

我必须同意 Ondrej 的观点。从风格的角度来看,如果你开始传递所有内容,ref最终你将与开发人员合作,他们会因为你设计这样的 API 而想掐死你!

只需从方法中返回内容,不要 100% 的方法返回void. 你所做的将会导致非常不干净的代码,并且可能会让最终处理你的代码的其他开发人员感到困惑。在这里优先考虑清晰度而不是性能,因为无论如何您都不会在优化中获得太多收益。

检查这个SO帖子:C#'ref'关键字,性能

Jon Skeet 的这篇文章:http://www.yoda.arachsys.com/csharp/parameters.html