声明变量时的性能问题

本文关键字:性能 问题 变量 声明 | 更新日期: 2023-09-27 18:01:38

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

这个例子只是为了说明这一点。

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看起来更好,更可读。

声明变量时的性能问题

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

这是你不能通过反射发现方法有哪些局部变量的核心原因。以及为什么在调试优化代码时会遇到问题。以及为什么c#中存在volatile关键字。局部变量在抖动优化器完成后就不再存在了。

您将在这个答案中找到由抖动执行的优化类型的概述。

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

嗯,我觉得选项1更有可读性。选项2意味着您正在操作这些属性的值。你需要花一到两秒钟的时间解析代码,才能发现你没有。

所以这只是一种风格的选择。参考您的风格指南,查看您的组织或特定的代码库更喜欢哪种风格。

性能差异将完全为零。在这一点上,您可能不相信我,如果不相信,那么唯一确定的方法就是亲自测试一下。但那真的是浪费时间。

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

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

但这正是为什么编译器要做任何类型的缓存,把选项1变成选项2,正如其他一些答案所暗示的那样。生成的代码会有所不同,但性能差异不会太大。(尽管JITter可能会在运行时执行这种类型的优化,正如Hans在他的回答中指出的那样)

两者的复杂度相同。所以我认为两者在两个方面都是平等的。

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

话虽如此,还是有区别的。属性get操作实际上是一个方法调用,因此第一个操作将调用person。get_FirstName方法。这意味着,根据编译器优化代码的方式,代码的行为可能会有所不同。

所以不会有任何可测量的差异,你应该选择最易读的选项。: -)