Relative Speed: F# v C# v VB.NET

(Spoiler alert: F# is faster – by alot)

As part of my presentation on F# which I hope to show at CodeCamp 2013, I did a quick speed test among C#, VB.NET, and F#.  I thought about Dr. Evil and how he tried to ransom the UN for “One Million Dollars”  and I thought about how the Federal Reserve is creating money from nothing to the tune of $40 Million a month via QE3 – so I built a quick app that does the exact same thing as the Federal Reserve does (except mine has unit tests, theirs probably does not).

I created a class for a Dollar bill (C#, VB, F#):

  1. public class Dollar
  2. {
  3.     public Int32 Id { get; set; }
  4.     public String SerialNumber { get; set; }
  5.     public Int32 FederalReserveDistrict { get; set; }
  6.     public Int32 SeriesDate { get; set; }
  7.     public String Signature { get; set; }
  8. }

  1. Public Class Dollar
  2.     Public Property Id As Int32
  3.     Public Property SerialNumber As String
  4.     Public Property FederalReserveDistrictNumber As Int32
  5.     Public Property SeriesDate As Int32
  6.     Public Property Signature As String
  7. End Class

  1. type dollar =
  2.     {Id: int;
  3.      SerialNumber: string;
  4.      federalReserveDistrict: int;
  5.      seriesDate: int;
  6.      signature: string}

I then implemented the same algorithm that the Federal reserve uses (C#, VB, F#):

  1. public List<Dollar> GetDollars(Int32 numberOfDollars)
  2. {
  3.     List<Dollar> dollars = new List<Dollar>();
  4.     Dollar currentDollar = null;
  5.     Random random = new Random();
  6.     for (int i = 0; i < numberOfDollars; i++)
  7.     {
  8.         currentDollar = new Dollar();
  9.         currentDollar.FederalReserveDistrict = random.Next(1, 13);
  10.         currentDollar.Id = i;
  11.  
  12.         String serialNumber = String.Empty;
  13.         for (int j = 0; j < 9; j++)
  14.         {
  15.             serialNumber += random.Next(0, 9).ToString();
  16.         }
  17.         currentDollar.SerialNumber = serialNumber;
  18.         currentDollar.SeriesDate = 2000 + random.Next(0, 10);
  19.         dollars.Add(currentDollar);
  20.     }
  21.     return dollars;
  22. }

  1. Public Function GetDollars(ByVal numberOfDollars As Int32) As List(Of Dollar)
  2.     Dim dollars As New List(Of Dollar)()
  3.     Dim currentDollar As Dollar = Nothing
  4.     Dim random As New Random()
  5.     For dollarIndex As Integer = 0 To numberOfDollars – 1
  6.         currentDollar = New Dollar()
  7.         currentDollar.FederalReserveDistrictNumber = random.Next(1, 13)
  8.         currentDollar.Id = dollarIndex
  9.  
  10.         Dim serialNumer As String = String.Empty
  11.         For serialNumberIndex As Integer = 0 To 9
  12.             serialNumer += random.Next(0, 9).ToString()
  13.         Next
  14.  
  15.         currentDollar.SerialNumber = serialNumer
  16.         currentDollar.SeriesDate = 2000 + random.Next(0, 10)
  17.         dollars.Add(currentDollar)
  18.     Next
  19.     Return dollars
  20. End Function

  1. type dollarProvider() =
  2.     let randomNumberGenerator = new System.Random()
  3.     let createSerialNumber =
  4.         List.init 9 (fun _ -> randomNumberGenerator.Next(0,9))
  5.                                     |> Seq.map string
  6.                                     |> String.concat ""
  7.     let createDollar id =
  8.         let returnDollar = {Id=id;
  9.             SerialNumber= createSerialNumber ;
  10.             federalReserveDistrict=randomNumberGenerator.Next(0,13);
  11.             seriesDate=2000+randomNumberGenerator.Next(0,9);
  12.             signature=""}
  13.         returnDollar
  14.     member this.GetDollars (numberOfDollars:int) =
  15.         List.init numberOfDollars (fun index -> createDollar index)

I then created a WPF I with the following XAML:

  1. <Grid>
  2.     <Button Content="Run C#" Height="32" HorizontalAlignment="Left" Margin="12,97,0,0" Name="CSharpButton" VerticalAlignment="Top" Width="95" Click="CSharpButton_Click" />
  3.     <TextBox Height="20" HorizontalAlignment="Left" Margin="12,150,0,0" Name="CSharpResultsTextBox" VerticalAlignment="Top" Width="95" />
  4.     <Button Content="Run VB" Height="29" HorizontalAlignment="Left" Margin="120,99,0,0" Name="RunVBButton" VerticalAlignment="Top" Width="99" Click="RunVBButton_Click" />
  5.     <TextBox Height="19" HorizontalAlignment="Left" Margin="117,150,0,0" Name="VBResultsTextBox" VerticalAlignment="Top" Width="102" />
  6.     <Button Content="Run F#" Height="28" HorizontalAlignment="Right" Margin="0,99,162,0" Name="RunFSharpButton" VerticalAlignment="Top" Width="105" Click="RunFSharpButton_Click" />
  7.     <TextBox Height="22" HorizontalAlignment="Left" Margin="239,147,0,0" Name="FSharpResultsTextBox" VerticalAlignment="Top" Width="102" />
  8.     <Label Content="Number Of Dollars" Height="30" HorizontalAlignment="Left" Margin="12,18,0,0" Name="label1" VerticalAlignment="Top" Width="115" />
  9.             <TextBox Height="24" HorizontalAlignment="Left" Margin="126,22,0,0" Name="DollarsTextBox" VerticalAlignment="Top" Width="115" Text="1000000" />
  10. </Grid>

and Code Behind

  1. private void CSharpButton_Click(object sender, RoutedEventArgs e)
  2. {
  3.     Stopwatch stopWatch = new Stopwatch();
  4.     stopWatch.Start();
  5.     CS.DollarProvider provider = new CS.DollarProvider();
  6.     Int32 numberOfDollars = Int32.Parse(this.DollarsTextBox.Text);
  7.     provider.GetDollars(numberOfDollars);
  8.     stopWatch.Stop();
  9.  
  10.     this.CSharpResultsTextBox.Text = stopWatch.Elapsed.TotalSeconds.ToString() + " seconds";
  11.  
  12. }
  13.  
  14. private void RunVBButton_Click(object sender, RoutedEventArgs e)
  15. {
  16.     Stopwatch stopWatch = new Stopwatch();
  17.     stopWatch.Start();
  18.     VB.DollarProvider provider = new VB.DollarProvider();
  19.     Int32 numberOfDollars = Int32.Parse(this.DollarsTextBox.Text);
  20.     provider.GetDollars(numberOfDollars);
  21.     stopWatch.Stop();
  22.  
  23.     this.VBResultsTextBox.Text = stopWatch.Elapsed.TotalSeconds.ToString() + " seconds";
  24.  
  25. }
  26.  
  27. private void RunFSharpButton_Click(object sender, RoutedEventArgs e)
  28. {
  29.     Stopwatch stopWatch = new Stopwatch();
  30.     stopWatch.Start();
  31.     FS.dollarProvider provider = new FS.dollarProvider();
  32.     Int32 numberOfDollars = Int32.Parse(this.DollarsTextBox.Text);
  33.     provider.GetDollars(numberOfDollars);
  34.     stopWatch.Stop();
  35.  
  36.     this.FSharpResultsTextBox.Text = stopWatch.Elapsed.TotalSeconds.ToString() + " seconds";
  37. }

 

And the results are pretty revealing:

image

 

I acknowledge that you can jerry rig C# to improve performance – esp using Pointers and the GOTO statement.  This exercise was to show performance doing the commonly-accepted way to solve a problem in each language (without FxCOP yelling at you).  It was not designed to see how clever you can get with C# to match the performance of the F# implementation.  As Rob Seder said “You can also bang in a screw by turning around the screwdriver and whacking away.”  If you need speed, F# is the best tool for the job.

2 Responses to Relative Speed: F# v C# v VB.NET

  1. Igor says:

    F# example is not comparable to C#.
    createSerialNumber will never get called in this test.
    if you carrect it and make a true function, F# example performance would get about 50% slower than C#.

  2. Ali says:

    I tried the VB code and it gave me the same result of c# 3.45 seconds.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: