Tasks and Program speed

Why is the uncommented piece of code so much faster than the commented version.  They both do the same thing (find prime numbers up to a given number.  The only difference is the uncommented section uses a task.


        private void FindThePrimes(object sender, EventArgs e)
        {
            Stopwatch s = new Stopwatch();
            s.Start();

            TextBox i = this.Controls["txtEnterPrime"] as TextBox;
            int MaxNumber = int.Parse(i.Text);
            //if (MaxNumber % 2 == 0)
            //{
            //    CheckPrimes a = new CheckPrimes();
            //    a.PrintListFromBeginning(MaxNumber / 2);
            //    a.PrintListFromEnd(MaxNumber, (MaxNumber / 2) + 1);
            //}
            //else
            //{
            //    CheckPrimes a = new CheckPrimes();
            //    a.PrintListFromBeginning((MaxNumber + 1) / 2);
            //    //GetListOfPrimesFromEnd b = new GetListOfPrimesFromEnd();
            //    a.PrintListFromEnd(MaxNumber, (MaxNumber + 3) / 2);

            //}

            if (MaxNumber % 2 == 0)
            {
                Task t1 = Task.Run(() =>
            {
                CheckPrimes a = new CheckPrimes();
                a.PrintListFromBeginning(MaxNumber / 2);


            });

                Task t2 = Task.Run(() =>
                {
                    CheckPrimes b = new CheckPrimes();
                    b.PrintListFromEnd(MaxNumber, (MaxNumber + 3) / 2);
                });
            }
            else
            {
                Task t1 = Task.Run(() =>
                {
                    CheckPrimes a = new CheckPrimes();
                    a.PrintListFromBeginning((MaxNumber) + 1 / 2);


                });

                Task t2 = Task.Run(() =>
                {
                    CheckPrimes b = new CheckPrimes();
                    b.PrintListFromEnd(MaxNumber, (MaxNumber + 3) / 2);
                });
            }
            s.Stop();

            txtMilliSeconds.Text = s.ElapsedMilliseconds.ToString();

        }

Open in new window


delegate bool myDelegate(int CheckThisNumber);
    struct CheckPrimes
    {
        public void PrintListFromBeginning(int MaxNumberToTry)
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(@"D:\OneDrive\Documents\MyPrograms\MathsPrograms\Prime1.csv", false))
                {
                    int CheckThisNumber = 2;
                    int Counter = 2;
                    for (CheckThisNumber = 2; CheckThisNumber <= MaxNumberToTry; CheckThisNumber++)
                    {
                        Counter = 2;
                        do
                        {
                            if (CheckThisNumber % Counter == 0)
                            {
                                break;
                            }
                            else
                            {

                            }
                            Counter += 1;
                        } while (Counter * Counter < CheckThisNumber);
                        if (Counter * Counter > CheckThisNumber)
                        {
                            sw.WriteLine(CheckThisNumber);
                            Counter = 2;
                        }
                        else
                        {
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadKey();
            }
        }
 
        public void PrintListFromEnd(int MaxNumberToTry, int MinNumberToTry)
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(@"D:\OneDrive\Documents\MyPrograms\MathsPrograms\Prime2.csv", false))
                {
                    int CheckThisNumber = 2;
                    int Counter = 2;
                    for (CheckThisNumber = MaxNumberToTry; CheckThisNumber >= MinNumberToTry; CheckThisNumber--)
                    {
                        Counter = 2;
                        do
                        {
                            if (CheckThisNumber % Counter == 0)
                            {
                                break;//CheckThisNumber not prime number
                            }
                            else
                            {

                            }
                            Counter += 1;
                        } while (Counter * Counter < CheckThisNumber);
                        if (Counter * Counter > CheckThisNumber)
                        {
                            sw.WriteLine(CheckThisNumber);
                            Counter = 2;
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadKey();
            }
        }
        public Boolean CheckIfNumberIsPrime(int CheckThisNumber)
        {
            int Counter = 2;

                Counter = 2;
                //for (Counter = 2; Counter*Counter <= CheckThisNumber; Counter++)
                do
                {
                    if (CheckThisNumber % Counter == 0)
                    {
                        return false;
                    }
                    else
                    {

                    }
                    Counter += 1;
                } while (Counter * Counter < CheckThisNumber);
                if (Counter * Counter > CheckThisNumber)
                {
                    return true;
                }
                else
                {
                    return false;
                }

        }
        public int GCDRecursive(int a, int b)
        {
            //article on http://www.vcskicks.com/euclidean-gcd.php
            if (a == 0)
                return b;
            if (b == 0)
                return a;

            if (a > b)
                return GCDRecursive(a % b, b);
            else
                return GCDRecursive(a, b % a);
        }
    }

Open in new window

AlHal2Asked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AndyAinscowFreelance programmer / ConsultantCommented:
>>The only difference is the uncommented section uses a task.

The difference in speed is due to the uncommented section using a task.  That splits the processing into two threads wheras without the task it will only use one thread (more work to do in the available bit of time given by the processor).
0
David FavorLinux/LXD/WordPress/Hosting SavantCommented:
Also the modulo operator (%) is one of the highest CPU usage functions you can call.
0
Chris StanyonWebDevCommented:
The Task runs asynchronously. You start the tasks and then move on, leaving them to run in the background. There's actually no guarantee that your tasks will have finished by the time you stop the timer, so the speed of operation may  bin in part just perception - the calculations could still be going on long after you've stopped the timer. Without a task, the timer isn't stopped until your calculations have finished - one after the other. If you need to use the result of tasks, then you should await them.
0
OWASP Proactive Controls

Learn the most important control and control categories that every architect and developer should include in their projects.

AlHal2Author Commented:
The commented section takes 2300 milliseconds to find prime numbers up to 5,000,000.  The uncommented section takes 10 milliseconds.
I don't see how multiple tasks can explain such a difference.

Both commented and uncommented sections use the modulo operator.
0
AlHal2Author Commented:
I just saw Chris' comment.  The code has a line
sw.stop
txtMilliSeconds.Text = s.ElapsedMilliseconds.ToString();

How do I update txtMilliSeconds.Text once both tasks have finished?
0
AndyAinscowFreelance programmer / ConsultantCommented:
I'd not actually looked in detail to see you were only timing how long one thread took, not to when you had the result.  (Using the task will still be faster overall).

Have a look at:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/await
0
AlHal2Author Commented:
The await command can only be used in an async method.
How do I do something similar with the lambda expression that I have?
Alternatively, how do I convert what I have to use Async methods?
0
Chris StanyonWebDevCommented:
The point of async / await is to keep your app responsive. You call a function. That function has some time consuming code. Without async / await, that code would block your app until it had finished. async / await helps with running it asynchronously, so your code fires it off on another thread. Once it's started the task it can carry on. This is what keeps your app responsive and is why your stopwatch is stopping really quick. All it's done is timed how long it took to start the tasks, not how long it took to finish them.

To time it properly, you need to await the tasks before stopping the timer, and as you've pointed out, you need to set your method up as async in order to use the await keyword:

private async Task FindThePrimes()
{
    var s = new Stopwatch();
    s.Start();

    var t1 = Task.Run(() =>
    {
        CheckPrimes a = new CheckPrimes();
        a.PrintListFromBeginning(MaxNumber / 2);
    });

    var t2 = Task.Run(() =>
    {
        CheckPrimes b = new CheckPrimes();
        b.PrintListFromEnd(MaxNumber, (MaxNumber + 3) / 2);
    });

    await Task.WhenAll(t1, t2);

    s.Stop();
    txtMilliSeconds.Text = s.ElapsedMilliseconds.ToString();
}

Open in new window

Now what will happen is that the timer will start. Your tasks will be fired off and start running. Your code will hit the await line, and at that point it will effectively return back to the caller of FindThePrimes. This allows you application to remain responsive while the tasks carry on running. Once both of your tasks have finished running, the code will pick up where it left off (the await line) and continue. It will stop the timer at that point and update your textbox.

It will still be quicker than your commented code, because your 2 tasks will be running in parallel in the background, rather than the second one waiting for the first to finish.

That's an over-simplified explanation, but hopefully you'll get the general idea.
0
AlHal2Author Commented:
How do I start the task given the current setup.  Previously the user pressed a button which created another label and textbox for the user to enter their number.  The code for the button press is this which I did so I could practice creating a control at runtime.

                private void cmdPrime_Click(object sender, EventArgs e)
        {
            Button b1 = new Button();
            b1.Text = "Click me";
            b1.Left = cmdPrime.Left + cmdPrime.Width;
            b1.Top = cmdPrime.Top;
            b1.Text = "Enter the number below which you want all primes";
            b1.Click += new EventHandler(FindThePrimes);//EventHandler is pre-defined delegate.  Right click and select goto definition
            ////Without the delegate I could not trigger an event for button added at runtime
            this.Controls.Add(b1);
            TextBox t1 = new TextBox();
            t1.Name = "txtEnterPrime";
            t1.Left = b1.Left + b1.Width + 10;
            t1.Top = b1.Top;
            t1.Size = new Size(100, 25);
            t1.KeyPress += new KeyPressEventHandler(TextBox1_KeyPress);
            this.Controls.Add(t1);
            var myTask = FindThePrimes();
            //myTask.Start();


        }


        }

Open in new window

0
Chris StanyonWebDevCommented:
OK. You'd need to set the return type to void and not Task:

private void cmdPrime_Click(object sender, EventArgs e)
{
    var b1 = new Button
    {
        Text = "Click me",
        Left = cmdPrime.Left,
        Top = cmdPrime.Top + cmdPrime.Height + 10,
        Width = cmdPrime.Width,
    };

    var t1 = new TextBox
    {
        Name = "txtEnterPrime",
        Left = b1.Left + b1.Width + 10,
        Top = b1.Top,
        Size = new Size(100, 25),
    };

    t1.KeyPress += TextBox1_KeyPress;
    b1.Click += FindThePrimes;

    this.Controls.Add(b1);
    this.Controls.Add(t1);
}

private async void FindThePrimes(object sender, EventArgs e)
{
    var s = new Stopwatch();
    s.Start();

    var t1 = Task.Run(() =>
    {
        CheckPrimes a = new CheckPrimes();
        a.PrintListFromBeginning(MaxNumber / 2);   
    });

    var t2 = Task.Run(() =>
    {
        CheckPrimes b = new CheckPrimes();
        b.PrintListFromEnd(MaxNumber, (MaxNumber + 3) / 2);
    });

    await Task.WhenAll(t1, t2);

    s.Stop();
    txtMilliSeconds.Text = s.ElapsedMilliseconds.ToString();
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AlHal2Author Commented:
Thanks very much.
0
AlHal2Author Commented:
I've signed off the project as it answers my question.  Just wondering how I would get this code to run without using a lambda.

var t1 = Task.Run(() =>
    {
        CheckPrimes a = new CheckPrimes();
        a.PrintListFromBeginning(MaxNumber / 2);   
    });

Open in new window

0
Chris StanyonWebDevCommented:
The Task.Run() method takes an Action as a parameter, so you can pass in anything that is defined as an Action:

private void cmdPrime_Click(object sender, EventArgs e)
{
    ...
    var t1 = Task.Run( new Action(SomeFunction) );

    Action someAction = SomeFunction;
    var t2 = Task.Run(someAction);
}

private void SomeFunction() {
    var a = new CheckPrimes();
    a.PrintListFromBeginning(MaxNumber / 2);  
}

Open in new window

The lambda just gives you ability to define an anonymous function to use as the action. If you want to remove the lambda, then you'll need to create a separate function.
0
AlHal2Author Commented:
Thanks again.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.