Channels ▼
RSS

.NET

PLINQ: Parallel Queries in .NET


Reduction

Reduction reduces a collection of elements to a singular value. For example, reduction could return an average value for a series of numbers. Other examples of reduction are calculating a summation, maximum value, or minimum value. In the .NET Framework, reduction is implemented as aggregation. LINQ provides a sequential implementation of aggregation, which runs on a single thread. This limits dependencies and is inherently thread-safe. LINQ provides explicit methods for the most common reductions, such as:

  • Sum
  • Average
  • Min
  • Max
  • Count

More Insights

White Papers

More >>

Reports

More >>

Webcasts

More >>

For other types of aggregation, you can implement a custom solution by using the Aggregate method.

The following is an example of reduction that uses LINQ. The reduction here is Count. It returns the number of positive values in a series of numbers.

var val=numbers.Where((number)=>number>-1).Count();

The next example is a custom aggregation and combines a collection of words as a sentence.

string [] words={
  "this", 
  "is", 
  "a", 
  "string", 
  "concatenation", 
  "."
};
string punctuation = ".,@#$%";
string delimiter="";
var sentence= words.Aggregate(
  (element1, element2) =>
  {
    delimiter = 
      punctuation.Contains(element2) ? "" : " ";
    return element1 + delimiter + element2;
  }
);

The Aggregate method accepts a delegate as a parameter. The preceding example implements this delegate as a lambda expression. The delegate has two parameters: the partial result, and the next element of the collection. The return value is a partial result. But the parameters to the first Aggregate invocation are different. For that first call, the parameters are the first and second elements of the collection, which in this case would be the strings this and is from the string array. For subsequent iterations, the first parameter would contain the intermediate result.

Reduction in a PLINQ query merges the results from multiple threads. This creates a potential dependency on both the source collection and the result. For this reason, each thread uses thread-local storage, which is non-shared memory, to cache partial results. When operations have completed, the separate partial results are combined into a final result. Like LINQ, PLINQ has the standard reductions, such as the Sum, Average, and Count methods. You simply need to add the AsParallel clause.

var val = numbers.AsParallel()
  .Where((number) => number > -1)
  .Count();

You can also perform custom aggregation by using the Aggregate method. The signature is different from the same method in LINQ. The following code is the simplest overload of the ParallelEnumerable.Aggregate method. The second parameter is the seed and is the first relevant parameter. Thread-local storage for threads used for caching partial results is initialized with the seed. The updateAccumulatorFunc function calculates the partial result for a thread. The combineAccumulatorsFunc function merges the partial results into a final result. The last parameter is resultSelector, which is used to perform a user-defined operation on the final results.

public static TResult Aggregate<TSource, 
                                  TAccumulate, Tresult>(
  this ParallelQuery<TSource> source, 
  TAccumulate seed, 
  Func<TAccumulate, TSource, TAccumulate> 
     updateAccumulatorFunc,
  Func<TAccumulate, TAccumulate, TAccumulate> 
     combineAccumulatorsFunc,
  Func<TAccumulate, TResult> resultSelector
)

Next is the first example of a PLINQ query that uses the Aggregate method. Reduction is being used to calculate a factorial, which is an individual value. This example calculates the factorial of 5 (5!). The answer is 120 (1 x 2 x 3 x 4 x 5). This example uses the Enumerable.Range method to return a collection of integrer values ranging from 1 to 5. In the Aggregate method, the partial result of each thread is initialized to 1. If initialized to zero, the results would immediately be nullified, because the product of zero and anything else is zero.

int value=5;
var factorial=Enumerable.Range(1, value)
  .AsParallel()
  .Aggregate(1, (result, number)=>
      result*number, result=>result);

In the following exercise, we use the Aggregate method to calculate the dot product of two matrices. We then list all three matrices: the two input matrices and one result matrix.

Multiply corresponding points in two matrices by using the Enumer­able.Aggregate method and save the product in a third matrix

1. Create a console application for C# in Visual Studio 2010. In the Main method, define a constant value of four. This is used to set the size of the one-dimensional matrices. In addition, declare and initialize two input matrices.

const int len = 4;
int[] first = new int [len] { 1, 2, 3, 4};
int[] second = new int[len] { 5, 6, 7, 8};

2. Starting at zero, create a range of integral values for indexes used with the matrices. Use the AsParallel clause to parallelize the Aggregate method.

var third=Enumerable.Range(0, first.Length).AsParallel()

3. You define an integer array as the first parameter, which is a reference. As such, each thread receives a reference to the same array. This is the result matrix and the initial value of each thread. Depending on how the array is used, this might not be thread-safe. In this circumstance, the different threads in the calculation will never access the same location in the result array. Therefore, it is thread-safe. The next parameter is a delegate. Insert a lambda expression that multiplies the two matrices, two elements at a time. The next parameter should merge the results of the various threads. Because the threads are sharing the same result matrix, a merge is not necessary. The final parameter is an action to be performed on the final result. In this example, there is no action.

.Aggregate(new int[len], (result, index) =>
{
  result.SetValue(first[index] * second[index], index);
  return result;
},
(total, subtotal) => total,
(result) => result);

4. Display the input and output matrices by using Console.WriteLine.

foreach (var element in first)
{
  Console.Write(element+" ");
}

Console.WriteLine();

foreach (var element in second)
{
  Console.Write(element+" ");
}

Console.WriteLine();

foreach (var element in third)
{
  Console.Write(element + " ");
}
Compile and run the program. Here is the entire application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Matrix

{
  class Program
  {
    static void Main(string[] args)
    {
      const int len = 4;
      int[] first = new int [len] { 1, 2, 3, 4};
      int[] second = new int[len] { 5, 6, 7, 8};
      var third=Enumerable.Range(0, first.Length)
        .AsParallel().Aggregate(new int[len], 
            (result, index)=>
        {
          result.SetValue(first[index]*second[index],
             index);
          return result;
        }, (result)=>result);

      foreach (var element in first)
      {
        Console.Write(element+" ");
      }

      Console.WriteLine();

      foreach (var element in second)
      {
        Console.Write(element+" ");
      }

      Console.WriteLine();

      foreach (var element in third)
      {
        Console.Write(element + " ");
      }

      Console.WriteLine("Press enter to exit");
      Console.ReadLine();
    }
  }
}

Figure 8 shows the output for the application. It lists the three relevant matrices.


Figure 8: Calculating the dot product of two matrices.


Related Reading






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video