Jump to content

How would i make this test multi threaded in C#?

AlTech

So,

 

I already setup some single threaded calculations via classes in C# DotNetCore.

 

One such calculation is doing Pythagoras. This is my current Pythagoras class.

using System;

namespace CSMark.Calculations
{
    class Pythagoras
    {
        private double opposite;
        private double adjacent;
        private double hypotenuse;
        private double hypotenuse_squared;
        private double adjacent_squared;
        private double opposite_squared;

        public void getOpposite(double _Hypotenuse, double _Adjacent)
        {
            hypotenuse = _Hypotenuse;
            adjacent = _Adjacent;
            opposite_squared = (hypotenuse * hypotenuse) - (adjacent * adjacent);
            opposite = Math.Sqrt(opposite_squared);
        }
        public void getAdjacent(double _Hypotenuse, double _Opposite)
        {
            hypotenuse = _Hypotenuse;
            opposite = _Opposite;
            adjacent_squared = (hypotenuse * hypotenuse) - (opposite * opposite);
            adjacent = Math.Sqrt(adjacent_squared);
        }
        public void getHypotenuse(double _Adjacent, double _Opposite)
        {
            adjacent = _Adjacent;
            opposite = _Opposite;
            hypotenuse_squared = (opposite * opposite) + (adjacent * adjacent);
            hypotenuse = Math.Sqrt(hypotenuse_squared);
        }

        public double returnAdjacent()
        {
            return adjacent;
        }
        public double returnOpposite()
        {
            return opposite;
        }
        public double returnHypotenuse()
        {
            return hypotenuse;
        }
    }

 

My Pythagoras Single threaded calculation currently is this.

using System;
using System.Diagnostics;
using CSMark.Calculations;

namespace CSMark.Benchmarks
{
    public class BenchPythagoras
    {
        Pythagoras py = new Pythagoras();
        Stopwatch stopwatch = new Stopwatch();
        int maxIterations = 500000000;
        int iteration = 0;
        //This what we'll use for H,O and A.
        double H = 10;
        double O = 8;
        double A = 6;
        string singleTime;
        string singleCalc;
      double singleC;
        double elapsedSingle;

        public string returnSingleScore()
        {
            singleTime = elapsedSingle.ToString() + " Seconds";
            return singleTime;
        }
        public string returnSingleScoreCalc()
        {
            singleC = maxIterations / elapsedSingle;

            //Convert raw value to thousands
            singleC = singleC / 1000;
            //Convert thousands to millions
            singleC = singleC / 1000;
            singleCalc = singleC.ToString() + " Million";
            return singleCalc;
        }
        public void singleThreadedBench()
        {
            Random random = new Random();
            stopwatch.Start();

            while (iteration <= maxIterations)
            {
                int randomNumber = random.Next(2);
                if (randomNumber == 0)
                {
                    py.getHypotenuse(A, O);
                }
                else if (randomNumber == 1)
                {
                    py.getOpposite(H, A);
                }
                else if (randomNumber == 2)
                {
                    py.getAdjacent(H, O);
                }
                else
                {
                    break;
                }
                //Increment the variables so that it's not the same each time.
                H = H + 3;
                O = O + 2;
                A = A + 1;
                //Increment our counter
                iteration++;
            }
            stopwatch.Stop();
            elapsedSingle = stopwatch.ElapsedMilliseconds / 1000;
            stopwatch.Reset();
        }
    }
}

Is there a way I can make something similar to the single threaded calculation but allow multiple threads to do the calculations?

 

The idea here is to see how much faster this would be if multiple cores or threads worked together to do the calculations.

 

I did a bit of research but didn't get very far with some of the concepts I saw on MSDN.

 

Thanks guys :).

Judge a product on its own merits AND the company that made it.

How to setup MSI Afterburner OSD | How to make your AMD Radeon GPU more efficient with Radeon Chill | (Probably) Why LMG Merch shipping to the EU is expensive

Oneplus 6 (Early 2023 to present) | HP Envy 15" x360 R7 5700U (Mid 2021 to present) | Steam Deck (Late 2022 to present)

 

Mid 2023 AlTech Desktop Refresh - AMD R7 5800X (Mid 2023), XFX Radeon RX 6700XT MBA (Mid 2021), MSI X370 Gaming Pro Carbon (Early 2018), 32GB DDR4-3200 (16GB x2) (Mid 2022

Noctua NH-D15 (Early 2021), Corsair MP510 1.92TB NVMe SSD (Mid 2020), beQuiet Pure Wings 2 140mm x2 & 120mm x1 (Mid 2023),

Link to comment
Share on other sites

Link to post
Share on other sites

@vorticalbox thoughts?

Judge a product on its own merits AND the company that made it.

How to setup MSI Afterburner OSD | How to make your AMD Radeon GPU more efficient with Radeon Chill | (Probably) Why LMG Merch shipping to the EU is expensive

Oneplus 6 (Early 2023 to present) | HP Envy 15" x360 R7 5700U (Mid 2021 to present) | Steam Deck (Late 2022 to present)

 

Mid 2023 AlTech Desktop Refresh - AMD R7 5800X (Mid 2023), XFX Radeon RX 6700XT MBA (Mid 2021), MSI X370 Gaming Pro Carbon (Early 2018), 32GB DDR4-3200 (16GB x2) (Mid 2022

Noctua NH-D15 (Early 2021), Corsair MP510 1.92TB NVMe SSD (Mid 2020), beQuiet Pure Wings 2 140mm x2 & 120mm x1 (Mid 2023),

Link to comment
Share on other sites

Link to post
Share on other sites

13 minutes ago, AluminiumTech said:

@vorticalbox thoughts?

in python you can create a pool and then hand it a list for it spread pool threads. c# has a similar thing https://msdn.microsoft.com/en-us/library/3dasc8as(v=vs.80).aspx but I have never used it.

                     ¸„»°'´¸„»°'´ Vorticalbox `'°«„¸`'°«„¸
`'°«„¸¸„»°'´¸„»°'´`'°«„¸Scientia Potentia est  ¸„»°'´`'°«„¸`'°«„¸¸„»°'´

Link to comment
Share on other sites

Link to post
Share on other sites

Honestly concurrency is something (almost) nobody should try to pick up on their own. It's an incredibly complicated concept and the likelihood for mistakes skyrockets. Basically the most important part about multi-threaded programming is: Does it REALLY need to be multi-threaded? If not, don't do it. Concurrency and all of its included concepts is something that really needs to be studied with help from a professor or professional. I can't even imagine doing it without being taught first because something as simple as deadlock can occur and without the proper knowledge you might have zero clue as to why or how it is happening.

 

That being said, C# threads probably (because I don't use C#) work very similar to Java's, in which you declare a thread object and assign it to a specific function. You'd probably want to have each thread do its own block of pythagoras iterations and then return when they complete their allotted section. As in, if you have 4 threads you'd have each one doing 125000000 calculations (500000000/4 = 125000000). Given how you're incrementing the variables at the end of each iteration you'd have to synchronize these increments since it's shared between the threads or come up with another way of ensuring that each calculation has different H, O, and A values. Otherwise you will have all sorts of race conditions going on in your code.

Link to comment
Share on other sites

Link to post
Share on other sites

Last year I had to do a presentation about multithreading in c#, here's the first proof of concept I wrote for thread pools. Hope it helps.

namespace EjemploThreadPool
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(2,5);
            for (int i =0;i<10;i++) {
                ThreadPool.QueueUserWorkItem(ThreadProc);
            }
            Console.Read();
        }
        static void ThreadProc(Object stateInfo)
        {
            Console.WriteLine("Empiezo a trabajar");
            Thread.Sleep(1000);
            Console.WriteLine("Termino de trabajar");
        }
    }
}

EDIT: After reading your code, you would have to make everything inside the while a process which is the task you would assign to the thread. You would have to make sure H, O and A are only accessed by one thread at any given moment. Consider using something like locks for the increment section.Since there is a limit to how many calculations you make, there's no special need to consider fairness (some process might get stuck in the lock but it's not an infinite task so eventually it will go through)

Finally, the stopwatch must be stopped either by the last process, or when all the processes are done. The easiest way is to keep a done process counter (also with exclusive access) and make the check at the end of every process

Edited by espurritado

The best way to measure the quality of a piece of code is "Oh F*** "s per line

Link to comment
Share on other sites

Link to post
Share on other sites

Do you actually want to split the work, or do you just want to make more than one thread do x iterations?

 

If splitting the work, you can precalculate the size of each part, or you can use something like a queue (more synchronization overhead) that all threads will be reading from.

 

Bottom line, there's no magic threading=yes parameter that makes things go faster, you have to think how you want the work to be split, and how you want to make sure these threads that are spinning in parallel are synchronized (e.g. locking mutexes or semaphores or queues/channels,  etc..) so you can make sure you get the result you want.

Link to comment
Share on other sites

Link to post
Share on other sites

7 hours ago, risk said:

Do you actually want to split the work, or do you just want to make more than one thread do x iterations?

I want to split the work.

7 hours ago, risk said:

If splitting the work, you can precalculate the size of each part, or you can use something like a queue (more synchronization overhead) that all threads will be reading from.

so I could divide the amount of work by the amount of threads and then get the threads to do their part but I would need to find a way for the threads to return something when completed?

7 hours ago, risk said:

Bottom line, there's no magic threading=yes parameter that makes things go faster,

ik

7 hours ago, risk said:

you have to think how you want the work to be split, and how you want to make sure these threads that are spinning in parallel are synchronized (e.g. locking mutexes or semaphores or queues/channels,  etc..) so you can make sure you get the result you want.

k

Judge a product on its own merits AND the company that made it.

How to setup MSI Afterburner OSD | How to make your AMD Radeon GPU more efficient with Radeon Chill | (Probably) Why LMG Merch shipping to the EU is expensive

Oneplus 6 (Early 2023 to present) | HP Envy 15" x360 R7 5700U (Mid 2021 to present) | Steam Deck (Late 2022 to present)

 

Mid 2023 AlTech Desktop Refresh - AMD R7 5800X (Mid 2023), XFX Radeon RX 6700XT MBA (Mid 2021), MSI X370 Gaming Pro Carbon (Early 2018), 32GB DDR4-3200 (16GB x2) (Mid 2022

Noctua NH-D15 (Early 2021), Corsair MP510 1.92TB NVMe SSD (Mid 2020), beQuiet Pure Wings 2 140mm x2 & 120mm x1 (Mid 2023),

Link to comment
Share on other sites

Link to post
Share on other sites

1 minute ago, gabrielcarvfer said:

@AluminiumTech you don't really need to do a lot of stuff as there's no data dependency there. Something like the following should solve your problem.

 


		public void multiThreadedBench()
        {
            stopwatch.Start();
            int maxThreadIterations = maxIterations / Environment.ProcessorCount;
            Thread[] workerThreads = new Thread[Environment.ProcessorCount];

            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                workerThreads[i] = new Thread(() => Program.threadCalc(H, O, A, maxThreadIterations));
                H += 3 * maxThreadIterations;
                O += 2 * maxThreadIterations;
                A += 1 * maxThreadIterations;
                workerThreads[i].Start();
            }

            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                workerThreads[i].Join();
            }
            stopwatch.Stop();
            elapsedMulti = stopwatch.ElapsedMilliseconds / 1000;
            stopwatch.Reset();
        }

        public static int threadCalc(double H, double O, double A, int maxThreadIterations)
        {
            Pythagoras py = new Pythagoras();
            Random random = new Random();
            int iteration = 0;

            while (iteration <= maxThreadIterations)
            {
                int randomNumber = random.Next(2);
                switch (randomNumber)
                {
                    case 0:
                        py.getHypotenuse(A, O);
                        break;
                    case 1:
                        py.getOpposite(H, A);
                        break;
                    case 2:
                        py.getAdjacent(H, O);
                        break;
                    default:
                        break;
                }

                //Increment the variables so that it's not the same each time.
                H = H + 3;
                O = O + 2;
                A = A + 1;
                //Increment our counter
                iteration++;
            }
            return 0;
        }

 

Ohhhhh.. I'll try something like that. Thanks :).

Judge a product on its own merits AND the company that made it.

How to setup MSI Afterburner OSD | How to make your AMD Radeon GPU more efficient with Radeon Chill | (Probably) Why LMG Merch shipping to the EU is expensive

Oneplus 6 (Early 2023 to present) | HP Envy 15" x360 R7 5700U (Mid 2021 to present) | Steam Deck (Late 2022 to present)

 

Mid 2023 AlTech Desktop Refresh - AMD R7 5800X (Mid 2023), XFX Radeon RX 6700XT MBA (Mid 2021), MSI X370 Gaming Pro Carbon (Early 2018), 32GB DDR4-3200 (16GB x2) (Mid 2022

Noctua NH-D15 (Early 2021), Corsair MP510 1.92TB NVMe SSD (Mid 2020), beQuiet Pure Wings 2 140mm x2 & 120mm x1 (Mid 2023),

Link to comment
Share on other sites

Link to post
Share on other sites

Yay. Thank you @gabrielcarvfer it works :D.

 

Single Threaded time came out to be 34 seconds and multi threaded time came out to 9 seconds.

 

Now i'm going to apply something similar for my other calculations

Judge a product on its own merits AND the company that made it.

How to setup MSI Afterburner OSD | How to make your AMD Radeon GPU more efficient with Radeon Chill | (Probably) Why LMG Merch shipping to the EU is expensive

Oneplus 6 (Early 2023 to present) | HP Envy 15" x360 R7 5700U (Mid 2021 to present) | Steam Deck (Late 2022 to present)

 

Mid 2023 AlTech Desktop Refresh - AMD R7 5800X (Mid 2023), XFX Radeon RX 6700XT MBA (Mid 2021), MSI X370 Gaming Pro Carbon (Early 2018), 32GB DDR4-3200 (16GB x2) (Mid 2022

Noctua NH-D15 (Early 2021), Corsair MP510 1.92TB NVMe SSD (Mid 2020), beQuiet Pure Wings 2 140mm x2 & 120mm x1 (Mid 2023),

Link to comment
Share on other sites

Link to post
Share on other sites

5 hours ago, gabrielcarvfer said:

@AluminiumTech you don't really need to do a lot of stuff as there's no data dependency there. Something like the following should solve your problem.

 

Indeed it does look like there's no data dependency here, but just out of interest one might want to look into "false sharing" where, even if there is no shared data, threads can clobber each other's cache lines and still kill performance. Never have different threads work on data that is close to each other in memory.

 

edit: Nice video explaining the issue, great if you have an hour to kill.: https://www.youtube.com/watch?v=WDIkqP4JbkE

 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×