【问题标题】:Boolean vs memory布尔与内存
【发布时间】:2015-01-06 09:54:00
【问题描述】:

我们在工作中讨论了代码设计,其中一个问题是在处理对布尔方法调用的响应时,如下所示:

bool ok = IsEverythingOK();

if(ok)
{
   //do somehthing
}

我的一个同事坚持我们跳过额外的变量 ok 并写

if(IsEverythingOK())
{
   //do somehthing
}

因为他说使用“bool ok”语句在记忆方面不好。

我们应该使用哪一个?

【问题讨论】:

  • 您不必担心。可以忽略不计。
  • 效率没有区别。我个人更喜欢第二种形式,因为我认为它更容易阅读,但这只是我的意见。不过,我可能会将函数重命名为 EverythingIsOK。
  • @500-InternalServerError 我觉得很奇怪你会这样命名一个函数(返回一个布尔值)。我工作过的所有地方都使用IsSomethingValid 或它的变体。
  • 如果你曾经使用过调试器,将鼠标悬停在 'ok' 变量上也很不错 :)
  • 当然,因为 bool 使用 32 位内存,最好不要创建局部变量。毕竟在标准的 8GB 机器上,这将浪费 268,435,456 分之一的可用内存。无论您做什么,都没有一百万个嵌套 if 的四分之一!

标签: c# memory boolean


【解决方案1】:

解释你的问题:

使用局部变量有成本吗?

由于 C# 和 .NET 设计精良,我的期望是使用您所描述的局部变量没有成本或成本可以忽略不计,但让我尝试通过一些事实来支持这种期望。

以下 C# 代码

if (IsEverythingOk()) {
  ...
}

将编译成这个(简化的)IL(开启优化)

call        IsEverythingOk
brfalse.s   AfterIfBody
... if body

使用局部变量

var ok = IsEverythingOk();
if (ok) {
  ...
}

你会得到这个优化(和简化)的 IL:

call        IsEverythingOk
stloc.0
ldloc.0
brfalse.s   AfterIfBody
... if body

从表面上看,这似乎效率稍低,因为返回值存储在堆栈中,然后被检索,但 JIT 编译器也会执行一些优化。

您可以在启用本机代码调试的情况下查看通过调试您的应用程序生成的实际机器代码。您必须使用发布版本执行此操作,并且您还必须关闭在模块加载时抑制 JIT 优化的调试器选项。现在您可以在要检查的代码中放置断点,然后查看反汇编。请注意,JIT 就像一个黑匣子,我在我的计算机上看到的行为可能与其他人在他们的计算机上看到的不同。考虑到该免责声明,我为这两个版本的代码获得的汇编代码是(在调用的执行方式上略有不同):

call        IsEverythingOk
test        eax,eax  
je          AfterIfBody

所以 JIT 会优化掉多余的不必要的 IL。实际上,在我最初的实验中,IsEverythingOk 方法返回了true,并且 JIT 能够完全优化分支。然后当我切换到在方法中返回一个字段时,JIT 将内联调用并直接访问该字段。

底线:您应该期望 JIT 至少优化诸如瞬态局部变量之类的简单事物,即使代码生成了一些看似不必要的额外 IL。

【讨论】:

    【解决方案2】:

    这完全取决于你是否在循环中使用ok 做任何事情。

    bool ok = IsEverythingOK();
    
    if(ok)
    {
       //do somehthing
       ok = IsEverythingOK();
    }
    

    假设您在循环中没有对ok 执行任何操作,您可能会发现 JIT 编译器会转:

    bool ok = IsEverythingOK();
    
    if(ok)
    {
       //do somehthing
    }
    

    ...进入本质:

    if(IsEverythingOK())
    {
       //do somehthing
    }
    

    ...无论如何。

    【讨论】:

    • 很抱歉,这是不对的。正如您在 disassambler 中看到的那样,尽管不再使用 ok,但编译器不会优化此代码。使用ok 实际上会生成更多代码。
    【解决方案3】:

    我会说这是偏好,除非您有统一的编码标准。另一个提供了好处。

    如果您期望或假设除 if 子句之外的修改,这很好。虽然它确实在创建变量时创建了一个堆栈条目,但它会在方法范围之后被释放。

    bool ok = IsEverythingOK();
    
    if(ok)
    {
       //do somehthing
    }
    

    如果您只想将其用作验证,那就太好了。虽然只有当你的方法名称很短时才好。但是假设您在使用类之前访问一个类,例如 _myLongNameInstance.IsEverythingOK() 这会降低可读性,我将使用第一个,但在其他条件下,我会选择直接 if。

    if(IsEverythingOK())
    {
       //do somehthing
    }
    

    【讨论】:

      【解决方案4】:

      如果您使用第一个解决方案,编译器当然会生成一些额外的 IL 代码步骤,因为它至少需要额外的 stlocldloc 命令,但如果只是出于性能原因,请忘记这些微秒(或纳秒) .

      如果ok 变量没有其他原因,我还是更喜欢第二种解决方案,因为它更易于阅读。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-17
        • 2017-04-08
        • 1970-01-01
        • 1970-01-01
        • 2017-10-15
        • 2011-04-13
        • 2014-03-30
        • 1970-01-01
        相关资源
        最近更新 更多