Reputation: 119
Let's suppose to do a method CountString
that, given an array of strings and a int, returns the number of strings whose length is greater than that int.
If I have to take advantage as far as possible of multicore hardware, is it enough to do:
public int CountString(string[] s, int i)
{
return s.AsParallel().Count( res => res.length > i);
}
or have i got to use somehow Tasks or even to mix task and PLinq??
It has to be considered only a simple example, i know this method not affects too much the hardware performance.
I'm wondering if it is better to do so, using AsParallel()
, or if it is better to declare the method async
and use the await
(even if i don't know how) in the method body.
Upvotes: 3
Views: 4879
Reputation: 149518
Edit:
As i see your actual question was a bit misleading, i'll try answering the intentional one. Specifically here, going with AsParallel
would be a good approach, since there isn't actually anything you need to await
. Since you're dealing with a collection, PLINQ or Paralle.ForEach are a good choice. Consider going with async-await
when you have naturally async I/O bound operations. It is recommended not to wrap synchronous methods with async wrappers.
If you actually test your code, you might even be surprised to see that paralleling this piece of code actually has a negative performance cost on your method execution, depending on the size of the array you're iterating over.
Many times people forget that using threads actually has overhead, even when using threads out of the threadpool. You have to have a least amount of CPU intensive work for this to be worth taking the performance hit of your parallelization.
If your array is long enough, then using AsParallel
should be suffice. There is no reason to be adding Task
as PLINQ will take care of the parallalization just fine.
Ok, lets actually test this code. Im going to be iterating over a string[]
filled with GUIDs. This is the code:
Main Method:
void Main()
{
//JIT
Test(0);
Test(100);
Test(1000);
Test(10000);
Test(1000000);
Test(10000000);
}
public void Test(int itemAmount)
{
string[] strings = Enumerable.Range(0, itemAmount).Select(i => Guid.NewGuid()
.ToString()).ToArray();
var stopWatch = Stopwatch.StartNew();
CountStringInParallel(strings, itemAmount);
stopWatch.Stop();
Console.WriteLine("Parallel Call: String amount: {0}, Time: {1}",
itemAmount, stopWatch.Elapsed);
stopWatch.Restart();
CountStringSync(strings, itemAmount);
stopWatch.Stop();
Console.WriteLine("Synchronous Call: String amount: {0}, Time: {1}",
itemAmount, stopWatch.Elapsed);
}
Parallel and Sync:
public int CountStringInParallel(string[] s, int i)
{
return s.AsParallel().Count( res => res.Length > i);
}
public int CountStringSync(string[] s, int i)
{
return s.Count(res => res.Length > i);
}
Results:
Parallel Call: String amount: 100, Time: 00:00:00.0000197
Synchronous Call: String amount: 100, Time: 00:00:00.0000026
Parallel Call: String amount: 1000, Time: 00:00:00.0000266
Synchronous Call: String amount: 1000, Time: 00:00:00.0000201
Parallel Call: String amount: 10000, Time: 00:00:00.0002060
Synchronous Call: String amount: 10000, Time: 00:00:00.0002003
Parallel Call: String amount: 1000000, Time: 00:00:00.0080492
Synchronous Call: String amount: 1000000, Time: 00:00:00.0135279
Parallel Call: String amount: 10000000, Time: 00:00:00.0744104
Synchronous Call: String amount: 10000000, Time: 00:00:00.1402474
You can see that up to 10,000 strings, the synchronous method is actually faster then the parallel.
Upvotes: 7
Reputation: 116518
As long as you don't use async-await
AsParallel
is all you need. There's no reason to use tasks directly as AsParallel
does that for you under the hood.
It's important to remember that parallelism has an overhead which in your case would probably be bigger than the added value of parallelism. For parallelism to actually improve performance, you should be working on many items, and the work itself should be non-trivial.
If you do, however, need to use async-await
AsParallel
(and the rest of PLinq) doesn't fit as it predates the TAP. You would need to use Task.Run
to parallelize the processing and Task.WhenAll
to await
for it all. Similar to this:
var tasks = items.Select(item => Task.Run(() => Process(item));
var results = await Task.WhenAll(tasks);
// process results
Upvotes: 1