C# 编译器可以折叠返回常量的函数吗?

标签 c# compilation

这是我用 BenchmarkDotNet 测试的程序。我正在比较 out 与元组的性能。

public class OutVsTuple
{
    [Benchmark]
    public void Out()
    {
        void OutLocal(out int a, out int b)
        {
            a = 1;
            b = 2;
        }

        OutLocal(out int x, out int y);
        int z = x + y;
    } 

    [Benchmark]
    public void Tuple()
    {
        (int a, int b) TupleLocal() => (1,2);

        (int x, int y) = TupleLocal();
        int z = x + y;
    } 
}

class Program
{
    static void Main(string[] args)
    {
        BenchmarkRunner.Run<OutVsTuple>();
    }
}

结果:

 Method |      Mean |     Error |    StdDev |    Median |
------- |----------:|----------:|----------:|----------:|
    Out | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
  Tuple | 5.3041 ns | 0.1422 ns | 0.3209 ns | 5.1754 ns |

我认为 Out 的时间是 0.0000,因为整个函数的结果都可以知道,这使得该方法没有意义。我将其更改为像这样使用 Random

public class OutVsTuple
{
    System.Random r = new System.Random();

    [Benchmark]
    public void Out()
    {
        void OutLocal(out int a, out int b)
        {
            a = r.Next(0,100);
            b = r.Next(0,100);
        }

        OutLocal(out int x, out int y);
        int z = x + y;
    } 

    [Benchmark]
    public void Tuple()
    {
        (int a, int b) TupleLocal() => (r.Next(0,100),r.Next(0,100));

        (int x, int y) = TupleLocal();
        int z = x + y;
    } 
}

class Program
{
    static void Main(string[] args)
    {
        BenchmarkRunner.Run<OutVsTuple>();
    }
}

结果:

 Method |     Mean |     Error |    StdDev |
------- |---------:|----------:|----------:|
    Out | 27.10 ns | 0.5799 ns | 1.2233 ns |
  Tuple | 28.52 ns | 0.5486 ns | 0.4863 ns |

现在结果看起来更合理了。

  1. 编译器是否像我猜测的那样有任何机制来折叠函数?
  2. 为什么像 out 那样结果很容易知道,元组函数需要时间?

附言。运行更多次后,第一种情况最终返回非零值,但仍然很低。

 Method |      Mean |     Error |    StdDev |
------- |----------:|----------:|----------:|
    Out | 0.0022 ns | 0.0080 ns | 0.0075 ns |
  Tuple | 5.0376 ns | 0.1484 ns | 0.1823 ns |

最佳答案

Why the tuple function takes time when the result can be easily known like the out case?

这是您的错误:元组版本实际上是在创建 ValueTuple 结构的新实例。

带有 out 参数的第一个版本相当于对常量的两个简单赋值。 元组版本实际上在概念上等同于此:

var newTuple = new ValueTuple(1, 2);
var a = newTuple.Item1;
var b = newTuple.Item2;

而且它需要更多时间,因为必须在运行时创建新实例,因此无法优化掉。

如果您尝试在 DataRow 中使用元组,您会发现它们不是常量:

[DataTestMethod]
[DataRow( (1, 2) )]  // won't compile
void SomeTest( (double, double) args ) { }

编译器会提示你需要常量表达式,而元组不需要。

关于C# 编译器可以折叠返回常量的函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49897252/

相关文章:

java - 如何编译要在 AWS Lambda 中使用的 C++ 库?

java - Maven : Deploy Local Project to Remote Server with dependencies

java - 编译前更改版本常量

c# - 将 List<T> 作为 IEnumerable<T> 发送到方法的一些问题

c# - 在自引用表中预加载 Linq to SQL 实体

c# - 是否有从字符串转换为构造函数类型的 DI 框架?

c# - 为什么我的 DTO 为空?

c++ - 前向声明库名称

python - Cythonize 没有 .pyx 可用的 .pyc

c# - => 运算符的作用是什么?