HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Relevant performance difference C#-VB.NET - Integer Division

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
divisiondifferencerelevantnetperformanceinteger

Problem

C# code

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:

Return imid 'Never reached; just to avoid the warning


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:

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_improved2


The differences are in the midpoint function:

C#:

ldarg.0 
ldarg.1 
ldarg.0 
sub 
ldc.i4.2 
div 
add 
ret


VB:

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
ret

Code Snippets

Return imid 'Never reached; just to avoid the warning
return 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_improved2
ldarg.0 
ldarg.1 
ldarg.0 
sub 
ldc.i4.2 
div 
add 
ret
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
ret

Context

StackExchange Code Review Q#42522, answer score: 5

Revisions (0)

No revisions yet.