Search code examples
c#performancewinformsconsole-applicationperformance-testing

Why do I have such a large performance difference between a Console app and WinForms?


I wrote some simple C# code to test if a number is a prime. I was surprised to see that there was a huge difference in performance when I ran the code in a WinForms app vs a console app.

The code is pretty simple:

ulong num = 18446744073709551557;
Stopwatch stopwatch = Stopwatch.StartNew();
if (num % 2 == 0)
{
    stopwatch.Stop();
    MessageBox.Show("Composite " + stopwatch.ElapsedMilliseconds.ToString());
    return;
}
for (ulong i = 3; i <= Math.Sqrt(num); i += 2)
{
    if (num % i == 0)
    {
        stopwatch.Stop();
        MessageBox.Show("Composite " + stopwatch.ElapsedMilliseconds.ToString());
        return;
    }
}
stopwatch.Stop();
MessageBox.Show("Prime " + stopwatch.ElapsedMilliseconds.ToString());

For the console app, instead of MessageBox.Show(), I use Console.WriteLine(). Now I would have thought the performance difference would be negligible. However, the console app seems to consistently perform worse than the windows form app.

Compiled by Visual Studio 2022, for the debug build (run without debugging), it takes ~36000 milliseconds for the winforms app, but ~156000 ms for the console app. For the release build (run without debugging), ~35500 ms for winforms and ~137000 ms for the console. (Interestingly, both seem to run slightly faster if run with debugging.) What is the reason for this discrepancy?


Solution

  • I could not reproduce the issue.

    Here is a summary of my findings running in release, and without the debugger (Ctrl-F5 in VS). Significant differences between 32-bit and 64-bit processes exist for both Console and WinForms.

    IDE Platform Console WinForms
    VS2017/NET 4.8 x32 43.94 sec 43.21 sec
    VS2017/NET 4.8 x64 4.43 sec 4.38 sec
    VS2022/NET 4.8 x32 43.62 sec 43.33 sec
    VS2022/NET 4.8 x64 4.56 sec 4.45 sec
    VS2022/NET 6.0 x64 4.46 sec 4.45 sec

    NOTE: The differences between console and winforms are within the noise.

    Code

    Identical code for both projects for the test

    internal static bool IsPrimeTest(ulong num)
    {
        if (num == 2)
        {
            return true;
        }
        if (num % 2 == 0)
        {
            return false;
        }
        for (ulong i = 3; i <= Math.Sqrt(num); i += 2)
        {
            if (num % i == 0)
            {
                return false;
            }
        }
        return true;
    }
    

    and the runner

    private void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        ulong x = 18446744073709551557ul;
        textBox1.Text = "Burn-in";
        Program.IsPrimeTest(x / 16);
        textBox1.Text = "Start timing";
        var sw = Stopwatch.StartNew();
        bool ok = Program.IsPrimeTest(x);
        sw.Stop();
        string bits = Environment.Is64BitProcess ? "x64" : "x32";
        textBox1.Text = $"Bits={bits}, Prime={ok}, Time={sw.Elapsed.TotalSeconds:f2} sec";
        button1.Enabled = true;
    }
    

    As fun side-note, a similar test with Intel Fortran produced Time = 3.80 sec, which is only slightly faster than C#. Granted, Fortran does not support unsigned integers, so the checking had to happen with full precision floating point numbers. Each mod(x,p)==0 call is translated into x/p == int(x/p).