patterncsharpMinor
Relevant performance difference C#-VB.NET - Integer Division
Viewed 0 times
divisiondifferencerelevantnetperformanceinteger
Problem
C# code
VB.NET code
```
Shared KEY_NOT_FOUND As Integer = -1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim A() As Integer = createArray(1, 100000)
Dim sw1 As Stopwatch = performCalcs(1, A)
Dim sw2 As Stopwatch = performCalcs(2, A)
Dim lag As TimeSpan = TimeSpan.FromTicks(sw1.Elapsed.Ticks - sw2.Elapsed.Ticks)
End Sub
Public Shared Function createArray(minVal As Integer, maxVal As Integer) As Integer()
If (minVal >= maxVal) Then Return Nothing
Dim A(maxVal - minVal) As Integer
Dim i As Integer = -1
For curVal As Integer = minVal To maxVal
i = i + 1
A(i) = curVal
Next
Return A
End Function
Private Function performCalcs(curFunction As Integer, A() As Integer) As Stopwatch
Dim sw As Stopwatch = New Stopwatch
Dim max As Integer = 10000
Dim count As Integer = 0
sw.Start()
While (count > 1)
If imin = imax OrElse A(imid) = key Then Return imid
If A(imid) key Then
Return binary_search(A, key, imin, imid - 1)
ElseIf A(imid) <
static int KEY_NOT_FOUND = -1;
private void Form1_Load(object sender, EventArgs e)
{
int[] A = createArray(1, 100000);
Stopwatch sw1 = performCalcs(1, A);
Stopwatch sw2 = performCalcs(2, A);
TimeSpan lag = TimeSpan.FromTicks(sw1.Elapsed.Ticks - sw2.Elapsed.Ticks);
}
public static int[] createArray(int minVal, int maxVal)
{
if (minVal >= maxVal) return null;
int[] A = new int[maxVal - minVal + 1];
int i = -1;
for (int curVal = minVal; curVal > 1);
if (imin == imax || A[imid] == key) return imid;
if (A[imid] key)
return binary_search(A, key, imin, imid - 1);
else if (A[imid] < key)
return binary_search(A, key, imid + 1, imax);
else
return imid;
}
}
private static int midpoint(int imin, int imax)
{
return imin + ((imax - imin) / 2);
}VB.NET code
```
Shared KEY_NOT_FOUND As Integer = -1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim A() As Integer = createArray(1, 100000)
Dim sw1 As Stopwatch = performCalcs(1, A)
Dim sw2 As Stopwatch = performCalcs(2, A)
Dim lag As TimeSpan = TimeSpan.FromTicks(sw1.Elapsed.Ticks - sw2.Elapsed.Ticks)
End Sub
Public Shared Function createArray(minVal As Integer, maxVal As Integer) As Integer()
If (minVal >= maxVal) Then Return Nothing
Dim A(maxVal - minVal) As Integer
Dim i As Integer = -1
For curVal As Integer = minVal To maxVal
i = i + 1
A(i) = curVal
Next
Return A
End Function
Private Function performCalcs(curFunction As Integer, A() As Integer) As Stopwatch
Dim sw As Stopwatch = New Stopwatch
Dim max As Integer = 10000
Dim count As Integer = 0
sw.Start()
While (count > 1)
If imin = imax OrElse A(imid) = key Then Return imid
If A(imid) key Then
Return binary_search(A, key, imin, imid - 1)
ElseIf A(imid) <
Solution
Now that we have code to review - the first issue that I see is that you are not comparing identical source code, and that makes it impossible to get an accurate benchmark. The problem is likely this line in the VB source that isn't in the C# source:
The C# code doesn't have a return statement outside of the loop. I don't have VB.NET installed in Visual Studio so I can't test it, but this is probably defeating optimization code in the compiler because it assumes that the return instruction can be reached. In fact, C# will give you a different warning if it is include in its source and warn you that unreachable code is detected if you include the corresponding code:
This illustrates the second issue - you are not actually benchmarking the code itself, but the performance of each of the underlying compilers. Assuming that the syntax is analogous between the two languages doesn't mean that it compiles the same in the C# compiler as it does in the VB.NET compiler. In fact, the different warnings between the two compilers make this quite obvious. Remember, just because the IDE is the same doesn't mean it does the same thing when you compile it. A proper benchmark has to be compiled under the same conditions. This is essentially like comparing benchmarks between identical C code compiled with gcc and Visual Studio.
I am curious how similar the compilers are though - try removing the return instruction (or adding one in the C# code), ignore the warnings, run the benchmarks again.
EDIT:
Finally had time to look at the IL, and the recursion doesn't have anything to do with the performance - both of them have exactly the same IL:
The differences are in the midpoint function:
C#:
VB:
Return imid 'Never reached; just to avoid the warningThe C# code doesn't have a return statement outside of the loop. I don't have VB.NET installed in Visual Studio so I can't test it, but this is probably defeating optimization code in the compiler because it assumes that the return instruction can be reached. In fact, C# will give you a different warning if it is include in its source and warn you that unreachable code is detected if you include the corresponding code:
return imid; //Never reached; gives warning "Unreachable code detected".This illustrates the second issue - you are not actually benchmarking the code itself, but the performance of each of the underlying compilers. Assuming that the syntax is analogous between the two languages doesn't mean that it compiles the same in the C# compiler as it does in the VB.NET compiler. In fact, the different warnings between the two compilers make this quite obvious. Remember, just because the IDE is the same doesn't mean it does the same thing when you compile it. A proper benchmark has to be compiled under the same conditions. This is essentially like comparing benchmarks between identical C code compiled with gcc and Visual Studio.
I am curious how similar the compilers are though - try removing the return instruction (or adding one in the C# code), ignore the warnings, run the benchmarks again.
EDIT:
Finally had time to look at the IL, and the recursion doesn't have anything to do with the performance - both of them have exactly the same IL:
default int32 binary_search_improved2 (int32[] A, int32 key, int32 imin, int32 imax) cil managed
{
// Method begins at RVA 0x6598
// Code size 46 (0x2e)
.maxstack 3
.locals init (
int32 V_0)
IL_0000: ldarg.3
IL_0001: ldarg.2
IL_0002: bge.s IL_000a
IL_0004: ldsfld int32 improvedBinary._Standard::KEY_NOT_FOUND
IL_0009: ret
IL_000a: ldarg.2
IL_000b: ldarg.3
IL_000c: ldarg.2
IL_000d: sub
IL_000e: ldc.i4.1
IL_000f: shr
IL_0010: add
IL_0011: stloc.0
IL_0012: ldarg.2
IL_0013: ldarg.3
IL_0014: beq.s IL_001c
IL_0016: ldarg.0
IL_0017: ldloc.0
IL_0018: ldelem.i4
IL_0019: ldarg.1
IL_001a: bne.un.s IL_001e
IL_001c: ldloc.0
IL_001d: ret
IL_001e: ldarg.0
IL_001f: ldloc.0
IL_0020: ldelem.i4
IL_0021: ldarg.1
IL_0022: bge.s IL_0029
IL_0024: ldloc.0
IL_0025: starg.s 2
IL_0027: br.s IL_000a
IL_0029: ldloc.0
IL_002a: starg.s 3
IL_002c: br.s IL_000a
} // end of method _Standard::binary_search_improved2The differences are in the midpoint function:
C#:
ldarg.0
ldarg.1
ldarg.0
sub
ldc.i4.2
div
add
retVB:
ldarg.0
conv.r8
ldarg.1
ldarg.0
sub.ovf
conv.r8
ldc.r8
div
call float64 [mscorlib]System.Math::Floor(float64)
add
call float64 [mscorlib]System.Math::Round(float64)
conv.ovf.i4
retCode Snippets
Return imid 'Never reached; just to avoid the warningreturn imid; //Never reached; gives warning "Unreachable code detected".default int32 binary_search_improved2 (int32[] A, int32 key, int32 imin, int32 imax) cil managed
{
// Method begins at RVA 0x6598
// Code size 46 (0x2e)
.maxstack 3
.locals init (
int32 V_0)
IL_0000: ldarg.3
IL_0001: ldarg.2
IL_0002: bge.s IL_000a
IL_0004: ldsfld int32 improvedBinary._Standard::KEY_NOT_FOUND
IL_0009: ret
IL_000a: ldarg.2
IL_000b: ldarg.3
IL_000c: ldarg.2
IL_000d: sub
IL_000e: ldc.i4.1
IL_000f: shr
IL_0010: add
IL_0011: stloc.0
IL_0012: ldarg.2
IL_0013: ldarg.3
IL_0014: beq.s IL_001c
IL_0016: ldarg.0
IL_0017: ldloc.0
IL_0018: ldelem.i4
IL_0019: ldarg.1
IL_001a: bne.un.s IL_001e
IL_001c: ldloc.0
IL_001d: ret
IL_001e: ldarg.0
IL_001f: ldloc.0
IL_0020: ldelem.i4
IL_0021: ldarg.1
IL_0022: bge.s IL_0029
IL_0024: ldloc.0
IL_0025: starg.s 2
IL_0027: br.s IL_000a
IL_0029: ldloc.0
IL_002a: starg.s 3
IL_002c: br.s IL_000a
} // end of method _Standard::binary_search_improved2ldarg.0
ldarg.1
ldarg.0
sub
ldc.i4.2
div
add
retldarg.0
conv.r8
ldarg.1
ldarg.0
sub.ovf
conv.r8
ldc.r8
div
call float64 [mscorlib]System.Math::Floor(float64)
add
call float64 [mscorlib]System.Math::Round(float64)
conv.ovf.i4
retContext
StackExchange Code Review Q#42522, answer score: 5
Revisions (0)
No revisions yet.