多个 return 变量 - 哪个具有最佳性能(输出、元组、class)?

Multiple return variables - which has best performance (out, tuple, class)?

实现returns多次双打的方法有多种:

通过out参数:

class MyClass
{
    static double Add3(double x, out double xp1, out double xp2)
    {
        xp1 = x + 1.0;
        xp2 = x + 2.0;
        return x + 3.0;
    }
}

通过元组:

class MyClass
{
    static Tuple<double, double, double> Add3(double x)
    {
        Tuple<double, double, double> ret = new Tuple<double, double, double>();
        ret.Item1 = x + 1.0;
        ret.Item2 = x + 2.0;
        ret.Item3 = x + 3.0;
        return ret;
    }

通过 class 收集结果:

class MyClass
{
    class Result
    { 
        double xp1;
        double xp2;
        double xp3;
    }

    static Result Add3(double x)
    {
        Result ret = new Result
        {
            xp1 = x + 1.0;
            xp2 = x + 2.0;
            xp3 = x + 3.0;
        }
        return ret;
    }
}

我对 this question 的评论的印象是,人们普遍认为带有额外 class 的方法是最佳实践。但是,我想知道是否有关于三种变体对 运行 时间性能的影响的经验法则。

out 参数相比,Tuple 或 class 的构造函数是否需要额外的时间?

特别是,在实际使用结果双打中只有一个的情况下,带有 out 参数的变体是否具有任何性能优势,例如在以下代码段中?

double zPlus3 = MyClass.Add3(z, out _, out _)

通过 Tuple<T1,T2,T3> or a custom class with 3 properties, is equivalent performance-wise. Tuples are more readily available (you don't have to code them) while custom classes are more convenient to use, but both approaches involve the instantiation of a reference-type, that has to be heap-allocated and later garbage collected. If you use these types just for accessing their properties once, then you are getting no added value to compensate for the heap-allocation/garbage collection overhead. Using out parameters is superior performance-wise in this case. There is a forth solution though, that combines the advantages of all these approaches: value tuples 返回多个值(在 C# 7.0 及更高版本中可用)。

static (double, double, double) Add3(double x)
{
    return (x + 1.0, x + 2.0, x + 3.0);
}

用法示例,演示 tuple deconstruction:

(double xp1, double xp2, double xp3) = Add3(13);

...或等效地使用类型推断:

var (xp1, xp2, xp3) = Add3(13);

优点:

  1. A ValueTuple<T1,T2,T3>Tuple<T1,T2,T3> 一样容易获得。
  2. 语言支持 changing the field namesValueTuple<T1,T2,T3>Item1Item2Item3 更有意义的东西,使它们(几乎)同样方便自定义 class.
  3. 一个ValueTuple<T1,T2,T3>入栈,和out参数一样。不涉及堆分配和垃圾收集。

为了向这个问题添加一些确凿的事实,here 是一个比较这些备选方案性能的基准项目。

令人惊讶的是,.NET 对 ValueTuple 和 KeyValuePair 进行了大量优化, 与调试相比,发布模式下的执行时间减少了 34 倍!

在发布模式下,所有实现都具有相似的速度, 除了返回 Tuple{int,int} 由于大量垃圾收集,速度慢了 10 倍。

在调试模式下,只有使用输出参数的方法是快速的。 从调试到发布构建的相对速度因子在最后一列'*'

中给出
Method Release Debug *
Return Tuple 510.84 ms 1,515.2 ms 3
Return KeyValuePair 44.56 ms 1,527.1 ms 34
Return ValueTuple 51.28 ms 1,418.6 ms 28
Return NullableValue 48.41 ms 1,527.0 ms 29
2 out Parameters 43.83 ms 560.4 ms 14
1 out Parameter 48.41 ms 586.5 ms 13
Return Single Value 49.72 ms 523.8 ms 11