声明变量时的性能问题

时间:2014-05-05 10:36:45

标签: c# performance

在下一种情况下声明新变量是否有任何性能成本:

这只是为了证明这一点。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

我有下一个方法:

选项1:

public void MyMethod(Person person)
{
    if (person.FirstName.Contains("Ro") || (person.LastName.StartsWith("A") && person.Age > 20))
    {
        //Do something
    }
    else if (person.FirstName.Contains("Ko") || (person.LastName.StartsWith("B") && person.Age >= 40))
    {
        //Do something
    }
    else if (person.FirstName.Contains("Mo") || (person.LastName.StartsWith("C") && person.Age > 60))
    {
        //Do something
    }
    else
    {
        //Do something
    }
}

选项2:

public void MyMethod(Person person)
{
    string firstName = person.FirstName;
    string lastName = person.LastName;
    int age = person.Age;

    if (firstName.Contains("Ro") || (lastName.StartsWith("A") && age > 20))
    {
        //Do something
    }
    else if (firstName.Contains("Ko") || (lastName.StartsWith("B") && age >= 40))
    {
        //Do something
    }
    else if (firstName.Contains("Mo") || (lastName.StartsWith("C") && age > 60))
    {
        //Do something
    }
    else
    {
        //Do something
    }
}

同样,这只是一个证明问题概念的例子。

问题:选项1和选项2之间是否存在任何性能或内存问题?

当然,选项2看起来更好,更具可读性。

4 个答案:

答案 0 :(得分:8)

这是由抖动解决的,它积极地消除了方法的局部变量,并寻找将它们存储在CPU寄存器中的方法。无论你是否自己声明变量,它都会这样做。你的属性getter很简单,没有副作用,抖动可以自己找到。这些getter方法也被删除了,它们是内联。实际上,它会将您的代码从第一个代码段转换为第二个代码段。

这是您无法通过Reflection找出方法所具有的局部变量的核心原因。为什么调试优化代码时遇到问题。为什么 volatile 关键字存在于C#中。当抖动优化器完成后,局部变量就不再存在了。

您将在this answer中找到抖动执行的优化类型的概述。

所以,不,不要犹豫,以这种方式使你的代码更具可读性,预计它不会产生任何影响。但请记住,您可以在代码中引入一个错误,当代码的其余部分影响您正在使用的对象时,它将触发。你当然会存储该物业的陈旧价值。如果类是非平凡的并且属性getter本身具有副作用,那么这并不总是那么明显。否则,.NET中的编码指南要求具有副作用的属性应该是方法的核心原因。

答案 1 :(得分:6)

嗯,我发现选项1更具可读性。选项2意味着您操纵这些属性的值。解析代码需要花费一两秒时间来判断你是不是。

所以它只是一种风格选择。请参阅您的样式指南,了解您的组织或特定代码库所偏爱的内容。

性能差异将完全为零。你可能在这方面不相信我,如果没有,唯一能确定的方法就是自己为它做基准测试。但这真的是浪费时间。

属性getter应该(除非文档另有说明)具有常量时间执行,因此查询属性的值应该与查询局部变量的值没有什么不同。因此,唯一可能影响性能的另一件事是,使用选项2,当代码可能永远不会到达需要最后一个的分支时,您最终可能会查询所有属性的值。姓名或年龄。但这可能会变化很大。您实际上只能通过使用一些真实数据重复进行基准测试来确定这一点。除非这种方法确实被证明是瓶颈,否则不要浪费你的时间。它不会成为。

如果你在某些基础上做出决定而不是可读性,那就是线程安全。如果属性getter返回的值可能会在另一个线程修改对象后发生更改,那么使用选项1可能会出现问题。这样可以将这些属性的值缓存到局部变量中,因此您可以选择选项2。

但这正是编译器将进行任何类型的缓存的原因,将选项1转换为选项2,正如其他一些答案所暗示的那样。生成的代码会有所不同,性能差异只会很大。 (虽然JITter肯定可以在运行时执行这种类型的优化,正如Hans在他的回答中指出的那样。)

答案 2 :(得分:0)

两者的复杂性是相同的。所以我认为两者都是平等的。

答案 3 :(得分:0)

在实践中,性能没有差异,我怀疑即使您运行了十亿次代码也无法衡量任何差异。

据说,有一点不同。属性get操作实际上是一个方法调用,因此第一个操作将在您访问该属性时调用person.get_FirstName方法。这意味着,根据编译器如何优化代码,可能会对代码的行为方式产生一些影响。

因此,不存在任何可测量的差异,您应该选择最易读的选项。 :-)