6

A coworker and I have differing opinions on If statements and their performance. My view is that If...ElseIf statements should be used. His view is that he doesn't believe in ElseIf, and writes everything with nested If statements.

Let's assume that a case statement cannot be used in this situation. What I am wondering is how efficiently code will be executed using nested If..Else statements versus using If...ElseIf statements. I know that code readability is a factor, but that shouldn't' affect performance.

Lets look at the following examples.

Using 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

Using ElseIf:

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

I know this is a small scale example, but lets say blocks like this are used heavily throughout the application.

Are there any performance differences between the two code sections, or would they perform almost identically once the application is compiled?

####UPDATE#####

I created a program to test running through the functions x number of times.

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

I ran the program and here are my results:
500 Loops Average - ElseIf: 108.248ms If Else: 106.507ms
1000 Loops Average - ElseIf: 107.747ms If Else: 107.451ms (Else If running first)
1000 Loops Average - ElseIf: 107.683ms If Else: 107.076ms (ElseIf running first)

Perhaps with a larger data set the numbers would change, but out of those 3 trials it actually appears that the If Else is outperforming the ElseIf statements.

Nicholas Post
  • 1,783
  • 1
  • 17
  • 28
  • 7
    There are no performance differences, just readability... – dcro Aug 29 '13 at 13:02
  • 2
    You are basically arguing whether blue or green is the best color... This has little to no impact on performance. – BentOnCoding Aug 29 '13 at 13:04
  • I agree with both drco and BentOnCoding. Hey, if your co-worker wants RPS or Carpal Tunnel, by all means, don't use 'ElseIf'. But I wouldn't recommend it (for code clarity reasons--although, health reasons aren't bad either ;) ) – fourpastmidnight Aug 29 '13 at 13:06
  • 5
    ILDASM. I'm betting it's identical. – peterG Aug 29 '13 at 13:17
  • 3
    Your co-worker is either hopelessly clueless about programming, or has other, deeply incompatible priorities when he writes code. On top of that, he does crazy things and spouts glib and preposterous lies to excuse himself. He's a dumb and malignant clown. Probably malicious, too, from what I know of the type. Kill it with fire, and don't give it a reference. I guarantee you he'll wind up in management if he sticks around, and then you'll be in real trouble. – 15ee8f99-57ff-4f92-890c-b56153 Aug 29 '13 at 13:18
  • 3
    `If Else` _is outperforming the_ `ElseIf` _statements._ Doubtful, the variance is so small. – James Sampica Aug 29 '13 at 14:05
  • In your test program since sleep(10) always returns false, the program will always go through all cases. You probably want to create a test where each case gets an equal distribution. – James Lawruk Aug 29 '13 at 14:26
  • 1
    I'm not convinced that a 0.274% difference in performance is sufficiently significant to be anything other than anomalous. – Jonathon Cowley-Thom Aug 30 '13 at 11:13

7 Answers7

8

I've decompiled the two and it seem to be generating the same code (using ildasm). This is a very simple If statment, might get different result for different If. I would suggest you do the same with your code and see.

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

And the other one

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

I would suggest to use the one that is easier to read.

the_lotus
  • 12,271
  • 2
  • 29
  • 50
  • Thanks for this, it's pretty much what I was looking for. Since they appear to compile the same, I'm guessing any differences I saw were due to the sleep function itself and not the statements. – Nicholas Post Aug 29 '13 at 15:34
  • 1
    @NicholasPost It's very difficult to benchmark very fast things. Maybe the cpu was a little bit busy with an other process. – the_lotus Aug 29 '13 at 15:38
7

You're worrying about the wrong things!!!

The code you write isn't the code that is executed. The compiler will modify the structure of your code for optimization, and it does very well with things such as this. That said, even if it didn't perform optimizations the speed difference wouldn't matter.

Don't worry about "is it as fast as it can be?" Instead worry about "is it fast enough, and as maintainable (readable) as it can be?".

Compilers and processors are very good at understanding logic structures, but meatbags (people) aren't. Whenever you write code you should try to make sure that it's as approachable and readable as possible. If you find that it's unacceptible slow then you can begin sacrificing readability for performance--but doing so out of paranoia is called "premature optimization", and it's a great way to make code that will be unmaintainable (and eventually spawn bugs).

With that said, here's some guidelines:

  • Methods with many ifs/elses are a code-smell (they have a high "cyclomatic complexity". It indicates that a single method does many this, which makes it hard to read, maintain, test, and alter. Decompose your method into many smaller methods. You may still end up with a relatively large "control" method, which determines what to do--but delegate the task of actually doing it to other methods.

  • Reduce nesting as much as possible.

    If there are cases that result in a simple return or exit then try to check them early in the sequence: (e.g. if (something) { return; }).

  • Group related checks together, and try to refactor them to their own method

  • Cover it all extensively in tests

STW
  • 40,454
  • 16
  • 100
  • 153
  • The question I asked isn't about readability or making it as fast as it can be, it is just to see if there really are any differences in execution. I agree that in the overall scheme of things, that the difference wouldn't be noticeable or affect performance enough to become an issue, and that there are many other factors to think of. I just want to know for my personal knowledge (perhaps on a deeper level of computing) what the differences are. – Nicholas Post Aug 29 '13 at 14:04
  • 3
    The question you asked is answered by the the_lotus, who decompiled a presumably representative case and found that the same code was generated for both -- and suggested you do the same with an actual case from your own code. If the compiler emits the same code for *logically* *equivalent* cases in both idioms, then any apparent performance difference is an artifact of something else happening on the system while you run the test. If your coworker doesn't understand that (which wouldn't surprise me), then you're wasting your time talking to him at all. – 15ee8f99-57ff-4f92-890c-b56153 Aug 29 '13 at 14:20
2

Well, I believe that it would all depend on the conditions you are checking.

For example (pseudo-code):

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

In this example, there's a common condition shared between all if statements, which means that re-writing to the following is probably more efficient:

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

However, if you cache the result of the A condition. The performance gains would probably minimal. Perhaps there are even optimizations performed by the compiler, so you would have to run a performance test to make sure there's even a difference in execution time.

More importantly, unless you are writing some performance-critical code, always try to write code by focusing on readability. There's almost always an efficient way of flattening conditionnal statements without hurting efficiency anyway.

plalx
  • 39,329
  • 5
  • 63
  • 83
  • 1
    There are definately compiler optimizations; doing things like caching results is a good strategy in some cases (e.g. if the result very expensive) but normally isn't needed. Writing, profiling, tuning (if needed) is the development methodology to follow – STW Aug 29 '13 at 15:08
0

It depends on your code.

The if statement is only accessed when a condition is met, otherwise it is ignored. the if elseif else block is the same but it is testing for many conditions and depending on which one is met a different action might have to be performed in order to get the results u want.

My opinion is "It depends upon cases".

If you want to execute everything in your code,then use elseif..

if you want to ignore something use if..

Sasidharan
  • 3,432
  • 3
  • 15
  • 35
0

i always prefer switch case when i have that many if elseif, but i know it's always possible. in that case the ElseIf always looks better, and is implemented in the background with else if, so it's should be the same performances.

but! for a lot of people, including some of my co-workers and bosses, it is not readable because the read it as if if if. i know it's crazy but it's some psychology thing i think.... so i understand where your coworker coming from

No Idea For Name
  • 10,935
  • 10
  • 37
  • 61
0

I ran a quick test and found that ElseIf runs slightly faster than Nested If. See the code below.

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
James Lawruk
  • 26,651
  • 19
  • 117
  • 128
0

From a performance perspective there is no meaningful difference. For me the readability of the ElseIf is clearly better.

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
dbasnett
  • 10,010
  • 2
  • 22
  • 32