多个返回变量-哪个具有最佳性能(out, tuple, class)?



返回多个双精度值的方法有多种实现方式:

通过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 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;
}
}

从对这个问题的评论中我的印象是,人们通常认为带额外类的方法是最佳实践。但是,我想知道关于这三种变体对运行时性能的影响是否有一条经验法则。

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

特别是,在实际只使用一个结果双精度数的情况下,例如下面的代码片段,带有out参数的变体是否具有任何性能优势?
double zPlus3 = MyClass.Add3(z, out _, out _)

为了给这个问题添加一些硬事实,这里有一个比较这些替代方案性能的基准测试项目。

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

在发布模式下,所有的实现都有相似的速度,除了返回Tuple{int,int}

由于高容量的垃圾收集,速度慢了10倍。在调试模式下,只有使用out - parameter的方法是快速的。从调试到发布版本的相对速度因子在最后一列'*'

中给出。
*返回元组510.84 ms1515.2 ms3返回KeyValuePair44.56 ms1,527.1 ms返回值51.28 ms1418.6 ms返回NullableValue48.41 ms43.83 ms560.4 ms48.41 ms586.5 ms返回单值49.72 ms523.8 ms

通过Tuple<T1,T2,T3>或具有3个属性的自定义类返回多个值,在性能方面是等效的。元组更容易获得(不需要编写代码),而自定义类更方便使用,但这两种方法都涉及引用类型的实例化,必须进行堆分配,然后进行垃圾收集。如果您使用这些类型只是为了访问它们的属性一次,那么您不会获得任何附加价值来补偿堆分配/垃圾收集开销。在这种情况下,使用out参数具有更好的性能。然而,还有第四种解决方案,它结合了所有这些方法的优点:值元组(在c# 7.0及更高版本中可用)。

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

使用示例,演示元组解构:

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

…或者等价地使用类型推断:

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

优势:

  1. ValueTuple<T1,T2,T3>Tuple<T1,T2,T3>一样容易获得。
  2. 语言支持将ValueTuple<T1,T2,T3>的字段名更改为比Item1,Item2Item3更有意义的东西,使它们(几乎)与自定义类一样方便。
  3. out参数一样,ValueTuple<T1,T2,T3>也存储在堆栈中。不涉及堆分配和垃圾收集。

最新更新