In a couple of previous posts, I explained how to use LINQ to get the longest word
from a text and how to get the number of repititions of a single word within a text. The following program combines the two, executes them both sequentially and parallelly
and dumps the results (and elapsed time) for both options:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Drawing;
using System.Diagnostics;
namespace ParallelProgramming.TaskPrograms
{
//The program then manipulates the images by using a sequential and a parallel loop
and displays both results
//which you can compare
class Program
{
static void Main(string[] args)
{
var words = GetWords();
ExecuteSequentially(words);
ExecuteParallelly(words);
}
static void ExecuteSequentially(IEnumerable<string> words)
{
var watch = Stopwatch.StartNew();
GetLongestWord(words);
GetCountForWord(words, "Processing");
watch.Stop();
Console.WriteLine("Sequential operations executed in {0} milliseconds", watch.ElapsedMilliseconds);
}
static void ExecuteParallelly(IEnumerable<string> words)
{
var watch = Stopwatch.StartNew();
Parallel.Invoke
(
() => GetLongestWord(words),
() => GetCountForWord(words, "Processing")
);
watch.Stop();
Console.WriteLine("Parallel operations executed in {0} milliseconds", watch.ElapsedMilliseconds);
}
static IEnumerable<string> GetWords()
{
var text = "This is a paragraph which needs to be analyzed for word processing. " +
"The term processing includes searching for most repeated words, the longest
word in this "
+
"text phrase and to find the number of times a given word is repeated in this
text.";
return text.Split(
new char[] { ' ', '\u000A', ',', '.', ';', ':', '-', '_', '/' },
StringSplitOptions.RemoveEmptyEntries);
}
private static string GetLongestWord(IEnumerable<string> words)
{
var longestWord = words.OrderByDescending(w => w.Length).First();
Console.WriteLine("The longest word is '{0}'.", longestWord);
return longestWord;
}
private static void GetCountForWord(IEnumerable<string> words, string term)
{
var findWord = words.Where(w => w.ToUpper().Contains(term.ToUpper()));
Console.WriteLine("The word '{0}' occurs {1} times.",
term, findWord.Count());
}
}
}
Parallel.Invoke takes in an array of Action delegates. Its signature looks like:
public static void Invoke( params Action[] actions) //There is another overload that
takes in a ParallelOptions
argument.
It executes each of these Action delegates, "potentially" as parallel tasks
to take advantage of multiple cores/processors.
Please note the word potentially. What this means is, with the Parallel.Invoke method
above, there's no gurantee that these
operations will be executed in parallel. The underlying task scheduler decides (based
on its own extensible algorithm) how
to schedule the tasks, on how many threads or whether to parallelize at all. It bases
it decision on factors like the
expensiveness of each operation, number of available cores, CPU cycles and others.
For example, the program above, on my
machine, produced the following results in one run:
---------------------------------------------------------
The longest word is 'processing'.
The word 'Processing' occurs 2 times.
Sequential operations executed in 17 milliseconds
The longest word is 'processing'.
The word 'Processing' occurs 2 times.
Parallel operations executed in 3 milliseconds
When I tried the same program, but this time around with two operations that calculates
the sum and maximum value from a
given set of 10000 integers, both produced 3 milliseconds.
The other overload of Paralle.Invoke takes an ParallelOptions argument using which
one can pass on a cancellation token,
control the degree of parallellism or even pass a custom Task schedulerto be used
for scheduling the parallel operations.