Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用ref而不是返回相同类型的性能成本是多少?_C#_Performance_Ref - Fatal编程技术网

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

C# 使用ref而不是返回相同类型的性能成本是多少?,c#,performance,ref,C#,Performance,Ref,嗨,这件事真的很困扰我,我希望有人能给我一个答案。我一直在读关于ref(和out)的书,我想知道我是否在使用refs来降低代码的速度。通常,我会替换如下内容: int AddToInt(int original, int add){ return original+add; } 与 因为在我看来 AddToInt(ref _value, _add); 比这更容易阅读和编码 _value = AddToInt(_value, _add); 我使用ref准确地知道我在代码上做什么,而不是返回值

嗨,这件事真的很困扰我,我希望有人能给我一个答案。我一直在读关于
ref
(和
out
)的书,我想知道我是否在使用
ref
s来降低代码的速度。通常,我会替换如下内容:

int AddToInt(int original, int add){ return original+add; }

因为在我看来

AddToInt(ref _value, _add);
比这更容易阅读和编码

_value = AddToInt(_value, _add);
我使用
ref
准确地知道我在代码上做什么,而不是返回值。然而,性能是我非常重视的,显然,当使用refs时,解引用和清理要慢得多

我想知道的是,在我看来,
ref
示例更小、更清晰、更准确的时候,为什么我读到的每一篇文章都会说,你通常会通过
ref
的地方很少(我知道这些示例是人为的,但我希望你能理解)


我也想知道为什么
ref
确实比返回值类型慢-在我看来,如果我在返回函数值之前对其进行大量编辑,在从内存中清除变量之前,引用实际变量来编辑它会比引用该变量的实例更快。

首先,不要担心使用
ref
是慢还是快。这是过早的优化。在99.9999%的情况下,您不会遇到导致性能瓶颈的情况


其次,与使用
ref
相反,返回计算结果作为返回值是首选的,因为类C语言通常具有“函数”性质。这将导致更好的语句/调用链接。

我必须同意Ondrej的观点。从风格上看,如果你开始使用
ref
传递所有东西,你最终会与开发人员合作,他们会因为设计这样的API而扼杀你

只需从方法返回内容,不要让100%的方法返回
void
。您所做的将导致非常不干净的代码,并可能会混淆最终使用您的代码的其他开发人员。在这里,你更喜欢清晰度而不是性能,因为你不会在验光中获得太多

查看此SO帖子:

Jon Skeet的这篇文章:

在与性能相同的句子中使用“ref”的主要时间是在讨论一些非常非典型的情况时,例如在XNA场景中,游戏“对象”通常由结构而不是类来表示,以避免GC的问题(这对XNA有不成比例的影响)。这有助于:

  • 防止在堆栈上多次复制过大的结构
  • 防止由于改变结构副本而导致数据丢失(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);

我认为最后一个是最不清楚的(后面紧跟着另一个“ref”),在不明确的语言(例如VB)中,ref用法更不清楚。

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

对于需要多个返回值并且为返回值构建特殊结构或类的情况,它非常有用(而且速度更快)。比如说,

public void Quaternion.GetRollPitchYaw(ref double roll, ref double pitch, ref double yaw){
    roll = something;
    pitch = something;
    yaw = something;
}
在指针使用不受限制的语言中,这是一种非常基本的模式。在c/c++中,您经常看到原语以类和数组作为指针按值传递。C#的作用正好相反,因此“ref”在上述情况下很方便

当您通过ref将要更新的变量传递到函数中时,只需执行一次写入操作即可得到结果。但是,当返回值时,通常会写入函数中的某个变量,然后返回,然后再次写入目标变量。根据数据的不同,这可能会增加不必要的开销。总之,这些是我在使用REF关键字之前通常考虑的主要内容。 有时,在c#中这样使用ref时,它的速度会稍微快一些,但不足以作为性能的goto理由

下面是我在一台7年历史的机器上得到的信息,使用下面的代码通过ref和value传递和更新100k字符串

输出:

迭代次数:10000000 byref:165ms 比瓦尔: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;
}
private void m\u btnTest\u单击(对象发送方,事件参数e){
秒表sw=新秒表();
字符串s=“”;
字符串值=新字符串('x',100000);//100k字符串
整数迭代次数=10000000;
//-----------------------------------------------------
//按ref更新
//-----------------------------------------------------
sw.Start();
对于(var n=0;nprivate 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;
}