Taking Advantage of Parallelism in .NET

Microservices, Data stream Processing, Event-Driven Architecture, CQRS… a lot of terms and buzzwords that represents the capacity of run process asynchronously in our softwares.

And they are good, but lets have a little break to think what kind of power we have right in our hands: the modern CPUs.

They usually have more than one physical core, and still manage to emulate more.

Recently, I read that Microsoft will launch a new version of its operation system: Windows 10 Workstations edition for power users. It will support 4 physical CPUs.

So, lets take advantage of it.

Usually we use to think algorithms in sequential steps, and that is right, because it depends of the result of each interaction to go to next step. But apps usually have a lot of algorithms running, and in some cases they can be independent of the others. Also we have typical cases where it is possible have an independent thread running in the system. For example I/O Bound.

Have you thought if is really necessary freeze the whole system waiting a conclusion of a HTTP communication? What about write a lot of data in disk? If you have to post the same data to five different HTTP endpoints, can it done parallel?

But for now, lets learn how to use it with C#.

Lets assume a classical mathematical problem, factorial computation:

static int GetFactorial(int n)
{
    return (n >= 1) ? (n * GetFactorial(n - 1)) : 1;
}

So, lets calculate factorial of 900, 100000 times, in a simple way:

static void Main(string[] args)
{
    //pause to stabilize the system
    Thread.Sleep(1000);
    
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    var number = 900;
    var timesToCalculate = 100000;

    // calculating N times the result of nuber factorial
    for (int i = 0; i < timesToCalculate; i++)
        GetFactorial(number);

    stopwatch.Stop();
    Console.WriteLine($"{number}! calculated {timesToCalculate} times took: {stopwatch.ElapsedMilliseconds}");
    Console.ReadLine();
}

And here we have the first result:

Let me introduce you the Task, a simple way to create threads in .NET.

There are a kind of “computer science philosophy” behind the difference and definitions, but a smart way to think can be: Tasks are easer to handle and work fine for computations with a result, like mathematical operations. Threads have a high cost to operation system, and a good use case can be something that you will constantly monitoring/handling.

Lets apply it to the code:

var tasks = new Task[timesToCalculate];

for (int i = 0; i < timesToCalculate; i++)
    tasks[i] = Task.Factory.StartNew(() => GetFactorial(number));

Task.WaitAll(tasks);

From the original 2202 milliseconds we were able to improve to 1687… seems we have some overhead costs, but even do it was an improvement.

Now I want to show you the “Parallel For”, a resource introduced in the .NET 4. When we have to do simple operations the results can be questioned (because some operations are so simple that does not pay the cost of divide the task among the cores).

Just to have an ideia:

static void Main(string[] args)
{
    var total = 1000000;
    var stopwatch = new Stopwatch();

    //pause to stabilize the system
    Thread.Sleep(1000);

    // normal for
    stopwatch.Start();
    for (int i = 0; i < total; i++) ;
    stopwatch.Stop();
    Console.WriteLine($"'normal for' elapsed milliseconds: {stopwatch.ElapsedMilliseconds}");

    //pause to stabilize the system
    Thread.Sleep(1000);

    stopwatch.Reset();

    // parallel for
    stopwatch.Start();
    Parallel.For(0, total, i => { });
    stopwatch.Stop();
    Console.WriteLine($"'parallel for' elapsed milliseconds: {stopwatch.ElapsedMilliseconds}");

    Console.ReadLine();
}

This code jus run empty for commands, and here are the results:

Metaphors:

One painter can took almost twice times to paint 300m² than two painter. But what about 1m²? Probably the second one will disturb more than help!

And in some cases there is no possible improve the process, for instance, 9 pregnant women can’t do a baby in one month.

But lets apply it to our Factorial scenario:

Parallel.For(0, timesToCalculate, (i) => {
    GetFactorial(number);
});

Now, lets see the new results:

How cool was that!? We manage to drop the time from 2202 milliseconds to 909 🙂

You probable are asking yourself what will happen if mix the parallel for with the Tasks? To be honesty, that does not make sense, because it will be thread over thread. Threads has overheads costs, thinks like sync results, monitoring, memory managements… so, use it wisely 😉

But it is really easy to apply parallelism using .NET. The framework can handle and calculate how to use the hardware properly. And if you have just one core, there is no error or problem.

And you can control it easy as well, for instance, using “parallel for” is possible set the max amount of core it will be used.

Also, you can spend some time taking a look at PLINQ.

Enjoy it.

GitHub

Spaki.

With more than 15 years of experience developing softwares and technologies, talking about startups, trends and innovation, today my work is focused to be CTO, Software Architect, Technical Speaker, Technical Consultant and Entrepreneur.

From Brazil, currently lives in Portugal working at https://www.farfetch.com as Software Architect, besides to keep projects in Brazil, like http://www.almocando.com.br/

Share

8 thoughts on “Taking Advantage of Parallelism in .NET”

Leave a Reply

Your email address will not be published. Required fields are marked *