【问题标题】:Performance Differences of Nested If Else Statements vs. ElseIf Statements嵌套 If Else 语句与 ElseIf 语句的性能差异
【发布时间】:2013-09-01 22:21:13
【问题描述】:

我和一位同事对If 的声明及其表现有不同的看法。我的观点是应该使用If...ElseIf 语句。他的观点是他不相信 ElseIf,并用嵌套的If 语句编写所有内容。

假设在这种情况下不能使用 case 语句。我想知道的是,使用嵌套的If..Else 语句与使用If...ElseIf 语句相比,代码执行效率如何。我知道代码可读性是一个因素,但这不应该影响性能。

让我们看看下面的例子。

使用 If Else:

If () then
    'Do something'
Else
    If () then
        'Do something'
    Else
        If () then
            'Do something'
        Else
            If () then
                'Do something'
            Else
                'Do something else'
            End If
        End If
    End If
End If

使用 ElseIf:

If () then
    'Do something'
ElseIf () then
    'Do something'
ElseIf () then
    'Do something'
ElseIf () then
    'Do something'
Else
    'Do something else'
End If

我知道这是一个小规模的例子,但可以说像这样的块在整个应用程序中被大量使用。

这两个代码段之间是否存在性能差异,或者在编译应用程序后它们的性能几乎相同?

####UPDATE#####

我创建了一个程序来测试通过函数运行 x 次。

Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    For i As Integer = 0 To 1000
        Run()
    Next
End Sub

Sub Run()

    Dim Time1Start As Integer = 0
    Dim Time1End As Integer = 0
    Dim Time2Start As Integer = 0
    Dim Time2End As Integer = 0

    Time2Start = CInt(DateTime.Now.ToString("fff"))
    runElse()
    Time2End = CInt(DateTime.Now.ToString("fff"))

    Time1Start = CInt(DateTime.Now.ToString("fff"))
    runElseIf()
    Time1End = CInt(DateTime.Now.ToString("fff"))

    TextBox1.Text += If(Time1End < Time1Start, Time1End + (1000 - Time1Start), Time1End - Time1Start) & vbTab & If(Time2End < Time2Start, Time2End + (1000 - Time2Start), Time2End - Time2Start) & vbCrLf
End Sub

Sub runElseIf()
    If sleep(10) Then
        'Do something'
    Else
        If sleep(10) Then
            'Do something'
        Else
            If sleep(10) Then
                'Do something'
            Else
                If sleep(10) Then
                    'Do something'
                Else
                    If sleep(10) Then
                        'Do something'
                    Else
                        If sleep(10) Then
                            'Do something'
                        Else
                            If sleep(10) Then
                                'Do something'
                            Else
                                If sleep(10) Then
                                    'Do something'
                                Else
                                    If sleep(10) Then
                                        'Do something'
                                    Else
                                        If sleep(10) Then
                                            'Do something'
                                        Else
                                            'Do something else'
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        End If
    End If
End Sub

Sub runElse()
    If sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    Else
        'Do something else'
    End If
End Sub

Function sleep(ByVal ms As Integer) As Integer
    System.Threading.Thread.Sleep(ms)
    Return False
End Function

End Class

我运行了程序,结果如下:
平均 500 次循环 - ElseIf:108.248ms If Else:106.507ms
平均 1000 次循环 - ElseIf:107.747ms If Else:107.451ms(Else 如果先运行)
1000 次循环平均 - ElseIf: 107.683ms If Else: 107.076ms (ElseIf 先运行)

也许使用更大的数据集,数字会发生变化,但在这 3 次试验中,If Else 实际上似乎优于ElseIf 语句。

【问题讨论】:

  • 没有性能差异,只是可读性...
  • 您基本上是在争论蓝色还是绿色是最好的颜色...这对性能几乎没有影响。
  • ILDASM。我敢打赌它是一样的。
  • 您的同事要么对编程一无所知,要么在编写代码时有其他严重不兼容的优先事项。最重要的是,他会做一些疯狂的事情,会说些油嘴滑舌和荒谬的谎言来为自己辩解。他是一个愚蠢而恶毒的小丑。据我所知,也可能是恶意的。以火杀之,别给它参考。我向你保证,如果他留下来,他最终会进入管理层,然后你就会遇到真正的麻烦。
  • If Else 优于 ElseIf 语句。值得怀疑,差异如此之小。

标签: .net vb.net if-statement


【解决方案1】:

这取决于你的代码。

if 语句仅在满足条件时才访问,否则将被忽略。 if elseif else 块是相同的,但它正在测试许多条件,并且根据满足哪个条件,可能必须执行不同的操作才能获得您想要的结果。

我的看法是“视情况而定”。

如果要执行代码中的所有内容,请使用 elseif..

如果您想忽略某些内容,请使用 if..

【讨论】:

  • 那么你是说如果我有和 ElseIf 语句,即使更高的一个变为 true,每个都会被评估?
  • @NicholasPost 性能没有区别!他们检查完全相同的条件!
【解决方案2】:

当我有那么多 if elseif 时,我总是更喜欢 switch case,但我知道这总是可能的。在那种情况下,ElseIf 总是看起来更好,并且在后台使用else if 实现,所以它应该是相同的性能。

但是!对于很多人来说,包括我的一些同事和老板,它是不可读的,因为它读为ififif。我知道这很疯狂,但我认为这是一些心理学问题....所以我明白你的同事来自哪里

【讨论】:

    【解决方案3】:

    我进行了快速测试,发现 ElseIf 的运行速度比嵌套 If 稍快。请参阅下面的代码。

    Imports System.Diagnostics
    Module Module1
    
        Sub Main()
            Dim sw As New Stopwatch()
            Dim nestedTotal As Integer
            sw.Start()
            For i = 1 To 100000
                Nested()
            Next
            sw.Stop()
            nestedTotal = sw.ElapsedMilliseconds
    
            sw.Reset()
    
            Dim elsesTotal As Integer
            sw.Start()
            For i = 1 To 100000
                Elses()
            Next
            sw.Stop()
            elsesTotal = sw.ElapsedMilliseconds
            Console.WriteLine("Nested If:" & nestedTotal)
            Console.WriteLine("ElseIf:" & elsesTotal)
            Console.Read()
        End Sub
    
        Sub Nested()
            Dim num As Integer = GetNum()
            If num = 1 Then
                DoSomething()
            Else
                If num = 2 Then
                    DoSomething()
                Else
                    If num = 3 Then
                        DoSomething()
                    Else
                        If num = 4 Then
                            DoSomething()
                        Else
                            DoSomething()
                        End If
                    End If
                End If
            End If
        End Sub
    
        Sub DoSomething()
            Dim j As Integer
            For i = 1 To 1000
                j = i + j
            Next
        End Sub
    
        Sub Elses()
            Dim num As Integer = GetNum()
            If num = 1 Then
                DoSomething()
            ElseIf num = 2 Then
                DoSomething()
            ElseIf num = 3 Then
                DoSomething()
            ElseIf num = 4 Then
                DoSomething()
            Else
                DoSomething()
            End If
        End Sub
    
        Function GetNum()
            Dim Generator As System.Random = New System.Random()
            Return Generator.Next(1, 5)
        End Function
    End Module
    

    【讨论】:

    • 我不认为使用 Console.Write 进行基准测试是个好主意。
    • 很公平。用你喜欢的任何代码替换 Console.Write。
    • Main() 中的最后几行打印嵌套 If 和 ElseIf 的经过毫秒数。
    【解决方案4】:

    好吧,我相信这一切都取决于您检查的条件。

    例如(伪代码):

    if (A && B) {
    } elseif (A && C) {
    } elseif (A && D) {
    }
    

    在此示例中,所有if 语句之间共享一个公共条件,这意味着重写以下内容可能更有效:

    if (A) {
        if (B) {
        } elseif (C) {
        } elseif (D) {
        }
    }
    

    但是,如果您缓存 A 条件的结果。性能提升可能很小。甚至可能还有编译器执行的优化,因此您必须运行性能测试以确保执行时间甚至存在差异。

    更重要的是,除非您正在编写一些对性能至关重要的代码,否则请始终尝试通过关注可读性来编写代码。几乎总有一种有效的方法可以在不影响效率的情况下扁平化条件语句。

    【讨论】:

    • 肯定有编译器优化;在某些情况下(例如,如果结果非常昂贵),缓存结果之类的事情是一个很好的策略,但通常不需要。编写、分析、调整(如果需要)是遵循的开发方法
    【解决方案5】:

    你担心错了!!!

    您编写的代码不是执行的代码。编译器会修改你的代码结构以进行优化,它对诸如此类的事情做得很好。也就是说,即使它不执行优化,速度差异也无关紧要。

    不用担心“它是否尽可能快?”而是担心“它是否足够快,并且尽可能可维护(可读)?”。

    编译器和处理器非常擅长理解逻辑结构,但肉袋(人)却不是。每当您编写代码时,您都应该尝试确保它尽可能平易近人且易于阅读。如果你发现它的速度慢得让人无法接受,那么你可以开始牺牲可读性来换取性能——但出于偏执而这样做被称为“过早优化”,这是一种使代码无法维护(并最终产生错误)的好方法。

    话虽如此,这里有一些指导方针:

    • 具有许多 ifs/else 的方法是代码异味(它们具有很高的“cyclomatic complexity”。这表明单个方法会执行很多此操作,这使得阅读、维护、测试和改变。将您的方法分解为许多较小的方法。您最终可能仍会得到一个相对较大的“控制”方法,它决定要做什么 - 但将实际执行的任务委托给其他方法。

    • 尽可能减少嵌套。

      如果有导致简单的return 或退出的情况,请尝试 在序列的早期检查它们:(例如if (something) { return; })。

    • 将相关检查组合在一起,并尝试将它们重构为自己的方法

    • 在测试中全面覆盖

    【讨论】:

    • 我问的问题不是关于可读性或让它尽可能快,它只是看看在执行上是否真的有任何差异。我同意在整体方案中,差异不会很明显或影响性能足以成为一个问题,并且还有许多其他因素需要考虑。我只是想知道我个人的知识(也许在更深层次的计算上)有什么区别。
    • the_lotus 回答了您提出的问题,他反编译了一个可能具有代表性的案例,发现两者都生成了相同的代码 - 并建议您对自己的代码中的实际案例做同样的事情.如果编译器在两个习惯用法中为 logical equivalent 情况发出相同的代码,那么任何明显的性能差异都是您运行测试时系统上发生的其他事情的产物。如果你的同事不明白这一点(这不会让我感到惊讶),那么你就是在浪费时间与他交谈。
    【解决方案6】:

    我已经对两者进行了反编译,它似乎生成了相同的代码(使用 ildasm)。这是一个非常简单的 If 语句,不同的 If 可能会得到不同的结果。我建议你对你的代码做同样的事情,然后看看。

    Module Module1
    
        Sub Main()
    
            Dim a As Integer
            Dim i As Integer = 1
    
            If i = 1 Then
                a = 9
            ElseIf i = 2 Then
                a = 8
            ElseIf i = 3 Then
                a = 7
            Else
                a = 6
            End If
    
        End Sub
    
    End Module
    
    
    
    .method public static void  Main() cil managed
    {
      .entrypoint
      .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
      // Code size       30 (0x1e)
      .maxstack  2
      .locals init ([0] int32 a,
               [1] int32 i)
      IL_0000:  ldc.i4.1
      IL_0001:  stloc.1
      IL_0002:  ldloc.1
      IL_0003:  ldc.i4.1
      IL_0004:  bne.un.s   IL_000b
      IL_0006:  ldc.i4.s   9
      IL_0008:  stloc.0
      IL_0009:  br.s       IL_001d
      IL_000b:  ldloc.1
      IL_000c:  ldc.i4.2
      IL_000d:  bne.un.s   IL_0013
      IL_000f:  ldc.i4.8
      IL_0010:  stloc.0
      IL_0011:  br.s       IL_001d
      IL_0013:  ldloc.1
      IL_0014:  ldc.i4.3
      IL_0015:  bne.un.s   IL_001b
      IL_0017:  ldc.i4.7
      IL_0018:  stloc.0
      IL_0019:  br.s       IL_001d
      IL_001b:  ldc.i4.6
      IL_001c:  stloc.0
      IL_001d:  ret
    } // end of method Module1::Main
    

    还有一个

    Module Module1
    
        Sub Main()
    
            Dim a As Integer
            Dim i As Integer = 1
    
            If i = 1 Then
                a = 9
            Else
                If i = 2 Then
                    a = 8
                Else
                    If i = 3 Then
                        a = 7
                    Else
                        a = 6
                    End If
                End If
            End If
    
        End Sub
    
    End Module
    
    .method public static void  Main() cil managed
    {
      .entrypoint
      .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
      // Code size       30 (0x1e)
      .maxstack  2
      .locals init ([0] int32 a,
               [1] int32 i)
      IL_0000:  ldc.i4.1
      IL_0001:  stloc.1
      IL_0002:  ldloc.1
      IL_0003:  ldc.i4.1
      IL_0004:  bne.un.s   IL_000b
      IL_0006:  ldc.i4.s   9
      IL_0008:  stloc.0
      IL_0009:  br.s       IL_001d
      IL_000b:  ldloc.1
      IL_000c:  ldc.i4.2
      IL_000d:  bne.un.s   IL_0013
      IL_000f:  ldc.i4.8
      IL_0010:  stloc.0
      IL_0011:  br.s       IL_001d
      IL_0013:  ldloc.1
      IL_0014:  ldc.i4.3
      IL_0015:  bne.un.s   IL_001b
      IL_0017:  ldc.i4.7
      IL_0018:  stloc.0
      IL_0019:  br.s       IL_001d
      IL_001b:  ldc.i4.6
      IL_001c:  stloc.0
      IL_001d:  ret
    } // end of method Module1::Main
    

    我建议使用更容易阅读的那个。

    【讨论】:

    • 谢谢你,这正是我想要的。由于它们似乎编译相同,我猜我看到的任何差异都是由于睡眠函数本身而不是语句。
    • @NicholasPost 很难对非常快的事物进行基准测试。也许 cpu 有点忙于其他进程。
    【解决方案7】:

    从性能的角度来看,没有有意义的差异。对我来说,ElseIf 的可读性显然更好。

    Private Sub xelseif(tries As Integer)
        Dim foo As Integer
        For x As Integer = 1 To tries
            For y As Integer = 1 To 5 Step 4
                If y = 1 Then
                    foo = y
                ElseIf y = 2 Then
                ElseIf y = 3 Then
                ElseIf y = 4 Then
                ElseIf y = 5 Then
                    foo = y
                End If
            Next
        Next
    End Sub
    
    Private Sub xelse(tries As Integer)
        Dim foo As Integer
        For x As Integer = 1 To tries
            For y As Integer = 1 To 5 Step 4
                If y = 1 Then
                    foo = y
                Else
                    If y = 2 Then
                    Else
                        If y = 3 Then
                        Else
                            If y = 4 Then
                            Else
                                If y = 5 Then
                                    foo = y
                                End If
                            End If
                        End If
                    End If
                End If
            Next
        Next
    End Sub
    
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim stpw As New Stopwatch
        Dim tries As Integer = 1500000
    
        xelse(10)
        stpw.Restart()
        xelse(tries)
        stpw.Stop()
        Debug.WriteLine(stpw.ElapsedMilliseconds)
    
        xelseif(10)
        stpw.Restart()
        xelseif(tries)
        stpw.Stop()
        Debug.WriteLine(stpw.ElapsedMilliseconds)
    End Sub
    

    【讨论】:

      猜你喜欢
      • 2021-02-23
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多