Does IF work better than IF-ELSE?

Which of these blocks of code works better, and which of them is more readable? I assume that the gain will be insignificant, especially in the second block. I'm just curious.

Block # 1

string height; string width; if (myFlag == 1) { height = "60%"; width = "60%"; } else { height = "80%"; width = "80%"; } 

Block # 2

 string height = "80%"; string width = "80%"; if (myFlag == 1) { height = "60%"; width = "60%"; } 



Update

The results, when I tested the above code, were that both blocks performed the same

Block # 1

 myFlag = 1: 3 Milliseconds myFlag = 0: 3 Milliseconds 

Block # 2

 myFlag = 1: 3 Milliseconds myFlag = 0: 3 Milliseconds 

But one important thing that I noticed here (thanks to Matthew Steeples answer here ) is that since the block of code I tested did not use the height and width variables, except for the assignment of if-else blocks and if blocks of Block -1 and 2, respectively, the compiler optimized the IL code by completely removing the if and if-else blocks, thereby showing incorrect results for our test here .

I updated both blocks of code to write the height and width values ​​to a file, using them again and causing the compiler to run our test blocks (hopefully), but you can observe from the code that the actual part of the file record does not affect the results of our tests.

These are updated results, C # and IL codes

results

Block # 1

 myFlag = 1: 1688 Milliseconds myFlag = 0: 1664 Milliseconds 

Block # 2

 myFlag = 1: 1700 Milliseconds myFlag = 0: 1677 Milliseconds 

C # .net Code

Block # 1

  public long WithIfAndElse(int myFlag) { Stopwatch myTimer = new Stopwatch(); string someString = ""; myTimer.Start(); for (int i = 0; i < 1000000; i++) { string height; string width; if (myFlag == 1) { height = "60%"; width = "60%"; } else { height = "80%"; width = "80%"; } someString = "Height: " + height + Environment.NewLine + "Width: " + width; } myTimer.Stop(); File.WriteAllText("testifelse.txt", someString); return myTimer.ElapsedMilliseconds; } 

Block # 2

  public long WithOnlyIf(int myFlag) { Stopwatch myTimer = new Stopwatch(); string someString = ""; myTimer.Start(); for (int i = 0; i < 1000000; i++) { string height = "80%"; string width = "80%"; if (myFlag == 1) { height = "60%"; width = "60%"; } someString = "Height: " + height + Environment.NewLine + "Width: " + width; } myTimer.Stop(); File.WriteAllText("testif.txt", someString); return myTimer.ElapsedMilliseconds; } 

IL code created by ildasm.exe

Block # 1

 .method public hidebysig instance int64 WithIfAndElse(int32 myFlag) cil managed { // Code size 144 (0x90) .maxstack 3 .locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer, [1] string someString, [2] int32 i, [3] string height, [4] string width, [5] string[] CS$0$0000) IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_0005: stloc.0 IL_0006: ldstr "" IL_000b: stloc.1 IL_000c: ldloc.0 IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_0012: ldc.i4.0 IL_0013: stloc.2 IL_0014: br.s IL_0070 IL_0016: ldarg.1 IL_0017: ldc.i4.1 IL_0018: bne.un.s IL_0029 IL_001a: ldstr "60%" IL_001f: stloc.3 IL_0020: ldstr "60%" IL_0025: stloc.s width IL_0027: br.s IL_0036 IL_0029: ldstr "80%" IL_002e: stloc.3 IL_002f: ldstr "80%" IL_0034: stloc.s width IL_0036: ldc.i4.5 IL_0037: newarr [mscorlib]System.String IL_003c: stloc.s CS$0$0000 IL_003e: ldloc.s CS$0$0000 IL_0040: ldc.i4.0 IL_0041: ldstr "Height: " IL_0046: stelem.ref IL_0047: ldloc.s CS$0$0000 IL_0049: ldc.i4.1 IL_004a: ldloc.3 IL_004b: stelem.ref IL_004c: ldloc.s CS$0$0000 IL_004e: ldc.i4.2 IL_004f: call string [mscorlib]System.Environment::get_NewLine() IL_0054: stelem.ref IL_0055: ldloc.s CS$0$0000 IL_0057: ldc.i4.3 IL_0058: ldstr "Width: " IL_005d: stelem.ref IL_005e: ldloc.s CS$0$0000 IL_0060: ldc.i4.4 IL_0061: ldloc.s width IL_0063: stelem.ref IL_0064: ldloc.s CS$0$0000 IL_0066: call string [mscorlib]System.String::Concat(string[]) IL_006b: stloc.1 IL_006c: ldloc.2 IL_006d: ldc.i4.1 IL_006e: add IL_006f: stloc.2 IL_0070: ldloc.2 IL_0071: ldc.i4 0xf4240 IL_0076: blt.s IL_0016 IL_0078: ldloc.0 IL_0079: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_007e: ldstr "testifelse.txt" IL_0083: ldloc.1 IL_0084: call void [mscorlib]System.IO.File::WriteAllText(string, string) IL_0089: ldloc.0 IL_008a: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_008f: ret } // end of method frmResearch::WithIfAndElse 

Block # 2

 .method public hidebysig instance int64 WithOnlyIf(int32 myFlag) cil managed { // Code size 142 (0x8e) .maxstack 3 .locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer, [1] string someString, [2] int32 i, [3] string height, [4] string width, [5] string[] CS$0$0000) IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_0005: stloc.0 IL_0006: ldstr "" IL_000b: stloc.1 IL_000c: ldloc.0 IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_0012: ldc.i4.0 IL_0013: stloc.2 IL_0014: br.s IL_006e IL_0016: ldstr "80%" IL_001b: stloc.3 IL_001c: ldstr "80%" IL_0021: stloc.s width IL_0023: ldarg.1 IL_0024: ldc.i4.1 IL_0025: bne.un.s IL_0034 IL_0027: ldstr "60%" IL_002c: stloc.3 IL_002d: ldstr "60%" IL_0032: stloc.s width IL_0034: ldc.i4.5 IL_0035: newarr [mscorlib]System.String IL_003a: stloc.s CS$0$0000 IL_003c: ldloc.s CS$0$0000 IL_003e: ldc.i4.0 IL_003f: ldstr "Height: " IL_0044: stelem.ref IL_0045: ldloc.s CS$0$0000 IL_0047: ldc.i4.1 IL_0048: ldloc.3 IL_0049: stelem.ref IL_004a: ldloc.s CS$0$0000 IL_004c: ldc.i4.2 IL_004d: call string [mscorlib]System.Environment::get_NewLine() IL_0052: stelem.ref IL_0053: ldloc.s CS$0$0000 IL_0055: ldc.i4.3 IL_0056: ldstr "Width: " IL_005b: stelem.ref IL_005c: ldloc.s CS$0$0000 IL_005e: ldc.i4.4 IL_005f: ldloc.s width IL_0061: stelem.ref IL_0062: ldloc.s CS$0$0000 IL_0064: call string [mscorlib]System.String::Concat(string[]) IL_0069: stloc.1 IL_006a: ldloc.2 IL_006b: ldc.i4.1 IL_006c: add IL_006d: stloc.2 IL_006e: ldloc.2 IL_006f: ldc.i4 0xf4240 IL_0074: blt.s IL_0016 IL_0076: ldloc.0 IL_0077: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_007c: ldstr "testif.txt" IL_0081: ldloc.1 IL_0082: call void [mscorlib]System.IO.File::WriteAllText(string, string) IL_0087: ldloc.0 IL_0088: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_008d: ret } // end of method frmResearch::WithOnlyIf 

So, we can say that the IF-Else block (Block # 1) is faster than the if (Block # 2) block , as many have indicated in this forum.

+64
performance c # if-statement
Oct 12 2018-11-12T00:
source share
12 answers

Test results

10,000,000 iterations Block 1

 myFlag = 0: 23.8ns per iteration myFlag = 1: 23.8ns per iteration 

10,000,000 iterations Block 2

 myFlag = 0: 23.8ns per iteration myFlag = 1: 46.8ns per iteration 

Block 2 is 96% slower than Block 1 . It makes sense, since Block 2 performs twice the work in the pessimistic case.

I prefer any case, depending on the situation. If myFlag rarely ever 1, then he wants it to stand out as the edge case that we need to handle. If both options are equally likely, I want the if-else syntax. But this is preference, not fact.




Decades ago, the Intel 80286 dual pipeline would have stopped if a conditional transition had been made, and not failed until the next instruction. By the time Pentium comes out; The CPU pre-uses both branch paths. But deep down, I still experience a fit of fear whenever I write code that has the most common result in the else clause. Every time I have to remind myself that it no longer matters.




 Int32 reps = 10000000; private void Block1(int myFlag) { String width; String height; Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < reps; i++) { if (myFlag == 1) { width = String.Format("{0:g}%", 60); height = String.Format("{0:g}%", 60); } else { width = String.Format("{0:g}%", 80); height = String.Format("{0:g}%", 80); } } sw.Stop(); Double time = (Double)sw.Elapsed.Ticks / Stopwatch.Frequency * 1000000000.0 / reps; MessageBox.Show(time.ToString() + " ns"); } private void Block2(int myFlag) { String width; String height; Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < reps; i++) { width = String.Format("{0:g}%", 80); height = String.Format("{0:g}%", 80); if (myFlag == 1) { width = String.Format("{0:g}%", 60); height = String.Format("{0:g}%", 60); } } sw.Stop(); Double time = (Double)sw.Elapsed.Ticks / Stopwatch.Frequency * 1000000000.0 / reps; MessageBox.Show(time.ToString() + " ns"); } 
  • String.Format makes IF slower by 96%
  • GetPercentageString(0.60) makes IF 96% slower



 const reps = 10000000; procedure Block1(myflag: Integer); var width, height: string; i: Integer; t1, t2: Int64; time: Extended; freq: Int64; begin QueryPerformanceCounter(t1); for i := 1 to reps do begin if myFlag = 1 then begin width := '60%'; height := '60%'; end else begin width := '80%'; height := '80%'; end; end; QueryPerformanceCounter(t2); QueryPerformanceFrequency(freq); time := (t2-t1) / freq * 1000000000 / reps; ShowMessage(FloatToStr(time)+ 'ns'); end; procedure Block2(myflag: Integer); var width, height: string; i: Integer; t1, t2: Int64; time: Extended; freq: Int64; begin QueryPerformanceCounter(t1); for i := 1 to reps do begin width := '80%'; height := '80%'; if myFlag = 1 then begin width := '60%'; height := '60%'; end; end; QueryPerformanceCounter(t2); QueryPerformanceFrequency(freq); time := (t2-t1) / freq * 1000000000 / reps; ShowMessage(FloatToStr(time)+ 'ns'); end; 



Performing twice the amount of work takes about twice as much time.

Answer: IF does not work better than IF-ELSE.




enter image description here

+92
Oct 12 '11 at 14:14
source share

The increase in performance here is negligible for the tune, which I would call this micro-micro-micro-optimization. Go for readability here if you don't plan on doing this a couple of million times.

Edit: (re: question in the comments)

In my opinion, the first one is more readable. It clearly shows in a ready-made format what lines should be for each case. The second excludes the case, so the reviewer will have to look at other areas of the code to determine the default value. To put this in perspective, imagine 50 lines of code between the original declaration / initialization and this particular block of code. If in this case it becomes unclear, then this will solve it for me.

+45
Oct. 12 2018-11-12T00:
source share

Update

After updating the code according to Matthew Steeples, answer and test the code in the Release assembly according to Lou Franco , I found that If-Else blcoks perfrom is better than if, albeit slightly

I used the following code blocks in my test application

C # .net Code

Block # 1

  public long WithIfAndElse(int myFlag) { Stopwatch myTimer = new Stopwatch(); string someString = ""; myTimer.Start(); for (int i = 0; i < 1000000; i++) { string height; string width; if (myFlag == 1) { height = "60%"; width = "60%"; } else { height = "80%"; width = "80%"; } someString = "Height: " + height + Environment.NewLine + "Width: " + width; } myTimer.Stop(); File.WriteAllText("testifelse.txt", someString); return myTimer.ElapsedMilliseconds; } 

Block # 2

  public long WithOnlyIf(int myFlag) { Stopwatch myTimer = new Stopwatch(); string someString = ""; myTimer.Start(); for (int i = 0; i < 1000000; i++) { string height = "80%"; string width = "80%"; if (myFlag == 1) { height = "60%"; width = "60%"; } someString = "Height: " + height + Environment.NewLine + "Width: " + width; } myTimer.Stop(); File.WriteAllText("testif.txt", someString); return myTimer.ElapsedMilliseconds; } 

Below are the results for the release build.

Results for 1,000,000 Iterations

Block # 1

 myFlag = 1: 1688 Milliseconds myFlag = 0: 1664 Milliseconds 

Block # 2

 myFlag = 1: 1700 Milliseconds myFlag = 0: 1677 Milliseconds 

IL code created by ildasm.exe

Block # 1

 .method public hidebysig instance int64 WithIfAndElse(int32 myFlag) cil managed { // Code size 144 (0x90) .maxstack 3 .locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer, [1] string someString, [2] int32 i, [3] string height, [4] string width, [5] string[] CS$0$0000) IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_0005: stloc.0 IL_0006: ldstr "" IL_000b: stloc.1 IL_000c: ldloc.0 IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_0012: ldc.i4.0 IL_0013: stloc.2 IL_0014: br.s IL_0070 IL_0016: ldarg.1 IL_0017: ldc.i4.1 IL_0018: bne.un.s IL_0029 IL_001a: ldstr "60%" IL_001f: stloc.3 IL_0020: ldstr "60%" IL_0025: stloc.s width IL_0027: br.s IL_0036 IL_0029: ldstr "80%" IL_002e: stloc.3 IL_002f: ldstr "80%" IL_0034: stloc.s width IL_0036: ldc.i4.5 IL_0037: newarr [mscorlib]System.String IL_003c: stloc.s CS$0$0000 IL_003e: ldloc.s CS$0$0000 IL_0040: ldc.i4.0 IL_0041: ldstr "Height: " IL_0046: stelem.ref IL_0047: ldloc.s CS$0$0000 IL_0049: ldc.i4.1 IL_004a: ldloc.3 IL_004b: stelem.ref IL_004c: ldloc.s CS$0$0000 IL_004e: ldc.i4.2 IL_004f: call string [mscorlib]System.Environment::get_NewLine() IL_0054: stelem.ref IL_0055: ldloc.s CS$0$0000 IL_0057: ldc.i4.3 IL_0058: ldstr "Width: " IL_005d: stelem.ref IL_005e: ldloc.s CS$0$0000 IL_0060: ldc.i4.4 IL_0061: ldloc.s width IL_0063: stelem.ref IL_0064: ldloc.s CS$0$0000 IL_0066: call string [mscorlib]System.String::Concat(string[]) IL_006b: stloc.1 IL_006c: ldloc.2 IL_006d: ldc.i4.1 IL_006e: add IL_006f: stloc.2 IL_0070: ldloc.2 IL_0071: ldc.i4 0xf4240 IL_0076: blt.s IL_0016 IL_0078: ldloc.0 IL_0079: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_007e: ldstr "testifelse.txt" IL_0083: ldloc.1 IL_0084: call void [mscorlib]System.IO.File::WriteAllText(string, string) IL_0089: ldloc.0 IL_008a: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_008f: ret } // end of method frmResearch::WithIfAndElse 

Block # 2

 .method public hidebysig instance int64 WithOnlyIf(int32 myFlag) cil managed { // Code size 142 (0x8e) .maxstack 3 .locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer, [1] string someString, [2] int32 i, [3] string height, [4] string width, [5] string[] CS$0$0000) IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_0005: stloc.0 IL_0006: ldstr "" IL_000b: stloc.1 IL_000c: ldloc.0 IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_0012: ldc.i4.0 IL_0013: stloc.2 IL_0014: br.s IL_006e IL_0016: ldstr "80%" IL_001b: stloc.3 IL_001c: ldstr "80%" IL_0021: stloc.s width IL_0023: ldarg.1 IL_0024: ldc.i4.1 IL_0025: bne.un.s IL_0034 IL_0027: ldstr "60%" IL_002c: stloc.3 IL_002d: ldstr "60%" IL_0032: stloc.s width IL_0034: ldc.i4.5 IL_0035: newarr [mscorlib]System.String IL_003a: stloc.s CS$0$0000 IL_003c: ldloc.s CS$0$0000 IL_003e: ldc.i4.0 IL_003f: ldstr "Height: " IL_0044: stelem.ref IL_0045: ldloc.s CS$0$0000 IL_0047: ldc.i4.1 IL_0048: ldloc.3 IL_0049: stelem.ref IL_004a: ldloc.s CS$0$0000 IL_004c: ldc.i4.2 IL_004d: call string [mscorlib]System.Environment::get_NewLine() IL_0052: stelem.ref IL_0053: ldloc.s CS$0$0000 IL_0055: ldc.i4.3 IL_0056: ldstr "Width: " IL_005b: stelem.ref IL_005c: ldloc.s CS$0$0000 IL_005e: ldc.i4.4 IL_005f: ldloc.s width IL_0061: stelem.ref IL_0062: ldloc.s CS$0$0000 IL_0064: call string [mscorlib]System.String::Concat(string[]) IL_0069: stloc.1 IL_006a: ldloc.2 IL_006b: ldc.i4.1 IL_006c: add IL_006d: stloc.2 IL_006e: ldloc.2 IL_006f: ldc.i4 0xf4240 IL_0074: blt.s IL_0016 IL_0076: ldloc.0 IL_0077: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_007c: ldstr "testif.txt" IL_0081: ldloc.1 IL_0082: call void [mscorlib]System.IO.File::WriteAllText(string, string) IL_0087: ldloc.0 IL_0088: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_008d: ret } // end of method frmResearch::WithOnlyIf 
+13
Oct 13 2018-11-11T00:
source share

You can answer this question yourself with the performance analyzer or just set the time for it (put the function in a loop that you call many times). As far as you know, the compiler turns this into the same code (you can check)

You probably should not worry about these micro-optimizations. Write the most readable code until your tools tell you what needs to be optimized.

+11
Oct 12 2018-11-12T00:
source share

As already mentioned, performance is unlikely to be a problem here if you are interested in readability, although you can try something like:

 string height = StdHeight; string width = StdWidth; if (restrictDimensionsFlag) { height = RestrictedHeight; width = RestrictedWidth; } 

and define your std and limited sizes as consts or readonlys elsewhere (or read from config).

+5
Oct 12 '11 at 14:15
source share

Correction of the result. I did my own test and I found this value, which I think is more accurate. No iterations: 100,000,000

Flag = 1

enter image description here

Flag = 0.

enter image description here

The fact that the worst time should be twice as good is incorrect.

Code used

 string height; string width; int myFlag = 1; Console.WriteLine(" ----------- case 1 ---------------"); DateTime Start = DateTime.Now; for (int Lp = 0; Lp < 100000000; Lp++) { if (myFlag == 1) { height = "60%"; width = "60%"; } else { height = "80%"; width = "80%"; } } TimeSpan Elapsed = DateTime.Now - Start; Console.WriteLine("Time Elapsed: {0} ms",Elapsed.Milliseconds); Console.WriteLine(" ----------- case 2 ---------------"); DateTime Start2 = DateTime.Now; for (int Lp = 0; Lp < 100000000; Lp++) { height = "80%"; width = "80%"; if (myFlag == 1) { height = "60%"; width = "60%"; } } Elapsed = DateTime.Now - Start2; Console.WriteLine("Time Elapsed: {0} ms", Elapsed.Milliseconds); 
+3
12 Oct 2018-11-12T00:
source share

Warning: It has been a while since I worked with certain CPU optimizations.

However, if I were to code this in assembly language, in block 1 there would be fewer instructions per loop than in block 2. At the assembly / machine code level, if / else is essentially free compared to if, because in any case, it is expanded essentially with the same instructions (loading, comparison, conditional transition).

Block1: best case: 5, worst: 6

 Load value of myFlag Compare to const 1 Jump if zero (equal) :t1 height = "80%"; width = "80%"; Jump :t2 :t1 height = "60%"; width = "60%"; :t2 

Block2: best case: 6, worst: 7

 height = "80%"; width = "80%"; Load value of myFlag Compare to const 1 Jump if non-zero (not-equal) :t1 height = "60%"; width = "60%"; :t1 

Cautions:

  • Not all instructions are created equal, and leaps, in particular, were more expensive when I studied assembly ... modern processors basically ended this trend.
  • Modern compilers do a HUGE amount of optimization and can change the structure of your code from any of these constructs to the equivalent of another or completely different method. (I saw a rather peculiar use of array indices, which could be used in such cases)

Conclusion: In general, the difference, even at the machine code level, will be very minimal between the two threads. Choose the one that works best for you and let the compiler know how best to optimize it. All relatively small cases like this should more or less be treated this way. Macro optimizations that change the amount of computation, for example, or reduce costly function calls, must be aggressively implemented. Small loop optimizations like this one are unlikely to make a real difference in practice, especially after the compiler can handle it.

+3
Oct 13 2018-11-11T00:
source share

A faster way to do this is probably treat the height and width as ints / floats and pass them into strings at the last second ... provided that you perform this operation often enough to deal remotely (hint: you're not).

+2
Oct 13 '11 at 18:12
source share

Looking at IL, I think you have a bigger problem than if the operator were faster. Since your methods have no side effects, the compiler actually deletes the contents of the if statements completely in debug mode and completely removes the if statements in release mode.

Opening the .exe file as ILSpy will verify this.

You will need to start with something that takes a known and constant amount of time before you find the answer to this question.

+2
Oct 14 '11 at 8:21
source share

I would use Block # 2 . As you said, the performance hit is negligible, but it is definitely shorter and easier to read. Essentially, you set default values ​​for your variables if a certain condition is not met.

0
Oct 12 2018-11-12T00:
source share

Block number 2 is more readable. However, does your company have coding standards? If so, I would follow them as much as possible or suggest consistent improvements.

In accordance with the performance in block No. 1, the height and width are initialized to zero values, but then assigned in any case (regardless of state). Almost zero performance difference.

Also, have you tested IL with ILDASM?

0
Oct 12 2018-11-12T00:
source share

I usually use the Block # 2 method only because I know that the variables in question are initially set by default

0
Oct 13 '11 at 12:53
source share



All Articles