Is C# slower than VB.NET?
- by Matt Winckler
Believe it or not, despite the title, this is not a troll.
Running some benchmarks this morning, my colleagues and I have discovered some strange things concerning performance, and I am wondering if we're doing something horribly wrong. We started out comparing C# vs. Delphi Prism calculating prime numbers, and found that Prism was about 30% faster. I figured maybe CodeGear did more optimization when generating IL (the exe was about twice as big as C#'s and had all sorts of different IL in it.) So I decided to write a test in VB.NET as well, assuming that Microsoft's compilers would end up writing essentially the same IL for each language. However, the result there was more shocking: C# was more than three times slower than VB running the same operations. The generated IL was different, but not extremely so, and I'm not good enough at reading it to understand the differences.
As a fan of C#, this apparent slowness wounds me horribly, and I am left wondering: what in the world is going on here? Is it time to pack it all in and go write web apps in Ruby? ;-)
I've included the code for each below--just copy it into a new VB or C# console app, and run. On my machine, VB finds 348513 primes in about 6.36 seconds. C# finds the same number of primes in 21.76 seconds. (I've got an Intel Core2 Quad Q6600 @2.4Ghz; on another Intel machine in the office the code for both runs much faster but the ratio is about the same; on an AMD machine here the timing is ~10 seconds for VB and ~13 for C#--much less difference, but C# is still always slower.)
Both of the console applications were compiled in Release mode, but otherwise no project settings were changed from the defaults generated by Visual Studio 2008.
Is it a generally-known fact that C#'s generated IL is worse than VB's? Or is this a strange edge case? Or is my code flawed somehow (most likely)? Any insights are appreciated.
VB code
Imports System.Diagnostics
Module Module1
Private temp As List(Of Int32)
Private sw As Stopwatch
Private totalSeconds As Double
Sub Main()
serialCalc()
End Sub
Private Sub serialCalc()
temp = New List(Of Int32)()
sw = Stopwatch.StartNew()
For i As Int32 = 2 To 5000000
testIfPrimeSerial(i)
Next
sw.Stop()
totalSeconds = sw.Elapsed.TotalSeconds
Console.WriteLine(String.Format("{0} seconds elapsed.", totalSeconds))
Console.WriteLine(String.Format("{0} primes found.", temp.Count))
Console.ReadKey()
End Sub
Private Sub testIfPrimeSerial(ByVal suspectPrime As Int32)
For i As Int32 = 2 To Math.Sqrt(suspectPrime)
If (suspectPrime Mod i = 0) Then
Exit Sub
End If
Next
temp.Add(suspectPrime)
End Sub
End Module
C# Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace FindPrimesCSharp {
class Program {
List<Int32> temp = new List<Int32>();
Stopwatch sw;
double totalSeconds;
static void Main(string[] args) {
new Program().serialCalc();
}
private void serialCalc() {
temp = new List<Int32>();
sw = Stopwatch.StartNew();
for (Int32 i = 2; i <= 5000000; i++) {
testIfPrimeSerial(i);
}
sw.Stop();
totalSeconds = sw.Elapsed.TotalSeconds;
Console.WriteLine(string.Format("{0} seconds elapsed.", totalSeconds));
Console.WriteLine(string.Format("{0} primes found.", temp.Count));
Console.ReadKey();
}
private void testIfPrimeSerial(Int32 suspectPrime) {
for (Int32 i = 2; i <= Math.Sqrt(suspectPrime); i++) {
if (suspectPrime % i == 0)
return;
}
temp.Add(suspectPrime);
}
}
}