Viacheslav Eremin | Visual Studio 2010 and .NET Framework 4 Training Kit | Next Step:

Developers can optimize their LINQ queries to execute in a parallel environment by utilizing Parallelized LINQ (PLINQ).

The Parallel Extensions library offers many different ways to implement parallelism in LINQ queries. PLINQ provides you with the System.Linq.ParallelEnumerable class which offers functionality similar to the System.Linq.Enumerable class.


Task 1 � Using the ParallelEnumerable Class� Static Methods to Parallelize LINQ

In this task, you will continue to use the same solution as the previous exercises.

  1. Open Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010 | Microsoft Visual Studio 2010.
  2. Open the solution file ParallelExtLab.sln located under Source\Ex04-PLINQ\begin(Choosing the folder that matches the language of your preference.)Optionally, you can continue working the solution you created in the previous exercise.
  3. Replace the current method calls from Main(), with a call to Ex4Task1_PLINQ().
    C#Copy Code
    static void Main(string[] args)
    {
        ...
        // Methods to call
        Ex4Task1_PLINQ();
        ...
    }
    

    Visual BasicCopy Code
    Sub Main(ByVal args() As String)
        ...
        ' Methods to call
        Ex4Task1_PLINQ()
        ... 
    End Sub
    

  4. Add the Ex4Task1_PLINQ() method to Program.cs (C#) or Module1.vb (Visual Basic):

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task1_PLINQ CSharp)

    C#Copy Code
    private static void Ex4Task1_PLINQ()
    {
        var q = Enumerable.Select(
                  Enumerable.OrderBy(
                    Enumerable.Where(employeeData, 
                    x => x.EmployeeID % 2 == 0),
                    x => x.EmployeeID),
                  x => PayrollServices.GetEmployeeInfo(x))
                  .ToList();
    
        foreach (var e in q)
        {
            Console.WriteLine(e);
        }
    }
    

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task1_PLINQ VB)

    Visual BasicCopy Code
    Private Sub Ex4Task1_PLINQ()
        Dim q = Enumerable.Select(
                    Enumerable.OrderBy(
                        Enumerable.Where(
                            employeeData,
                            Function(x) x.EmployeeID Mod 2 = 0),
                            Function(x) x.EmployeeID),
                            Function(x) PayrollServices.GetEmployeeInfo(x)) _
                .ToList()
    
        For Each e In q
            Console.WriteLine(e)
        Next e
    End Sub
    

    Note:
    The Select(), OrderBy(), and Where() methods are extension methods off of the IEnumerable generic class, however you are accessing them in a static manner here. You will try a more succinct usage later.
    The ToList() call is for illustrative purposes and is not always necessary in production code. It is used here because you want to immediately fire the LINQ query to collect all of the Employee Info strings, and then write them out to screen later.
    If you were to leave the ToList() off, the query will still fire in order of the Employee ID but each call to GetEmployeeInfo() wouldn�t fire until the IEnumerable generic is iterated through during the foreach loop. This is known as Delayed Execution.
    See Scott Wisniewski�s article at http://msdn.microsoft.com/en-us/magazine/cc163378.aspx for more information.


  5. Build and run the application.
  6. You should observe that the LINQ query performs the operations in order of the Employee ID. Also observe the total amount of time required to complete the work (the exact time required will vary):

    Figure 13
    Output from a non-parallelized LINQ query


  7. It is easy to parallelize this query by making use of the static ParallelEnumerable class�s version of the same LINQ methods. Additionally you�ll need to add an AsParallel() call to the query�s data source. Modify the Main() method just to call the PLINQAsParallel method.
    C#Copy Code
    static void Main(string[] args)
    {
        ...
        // Methods to call
        Ex4Task1_PLINQAsParallel();
        ...
    }
    

    Visual BasicCopy Code
    Sub Main(ByVal args() As String)
        ...
        ' Methods to call
        Ex4Task1_PLINQAsParallel()
        ... 
    End Sub
    

  8. Add the Ex4Task1_PLINQAsParallel() to Program.cs (C#) or Module1.vb (Visual Basic):

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task1_PLINQAsParallel CSharp)

    C#Copy Code
    private static void Ex4Task1_PLINQAsParallel()
    {
        var q = ParallelEnumerable.Select(
                ParallelEnumerable.OrderBy(
                  ParallelEnumerable.Where(employeeData.AsParallel(),
                    x => x.EmployeeID % 2 == 0),
                  x => x.EmployeeID),
                x => PayrollServices.GetEmployeeInfo(x))
              .ToList();
    
        foreach (var e in q)
        {
            Console.WriteLine(e);
        }
    }
    

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task1_PLINQAsParallel VB)

    Visual BasicCopy Code
    Private Sub Ex4Task1_PLINQAsParallel()
        Dim q = ParallelEnumerable.Select(
                    ParallelEnumerable.OrderBy(
                        ParallelEnumerable.Where(
                            employeeData.AsParallel(),
                            Function(x) x.EmployeeID Mod 2 = 0),
                            Function(x) x.EmployeeID),
                            Function(x) PayrollServices.GetEmployeeInfo(x)) _
                .ToList()
    
        For Each e In q
            Console.WriteLine(e)
        Next e
    End Sub
    

    Note:
    The calls to the Select(), OrderBy(), and Where() methods, which were previously being made on Enumerable, are now being made on ParallelEnumerable. Also notice that a call to AsParallel() has been added to the data source.


  9. Build and run the application.
  10. You should observe the LINQ query no longer performs the operations in a particular order. Also note that in this example the parallelized version completes in less time than the non-parallelized version (in this case, it completed in roughly half the time due to it being run on a dual-core machine; your results will vary based on the hardware you run this example on).

    Figure 14
    Output from a parallelized LINQ query


    Note:
    The operations are executed in parallel with as many operations occurring concurrently as the number of physical cores will allow.


Task 2 � Using the ParallelEnumerable Class� Extension Methods to Parallelize LINQ

As mentioned earlier, a more succinct way to take advantage of the Enumerable and ParallelEnumerable classes� static LINQ methods is to use them as Extension methods.

  1. Converting a non-parallelized LINQ query implemented using extension methods to a PLINQ query is straight forward. Replace the PLINQ query in the Main() method to match the following LINQ query:
    C#Copy Code
    static void Main(string[] args)
    {
        ...
        // Methods to call
        Ex4Task2_Extensions();
        ...
    }
    

    Visual BasicCopy Code
    Sub Main(ByVal args() As String)
        ...
        ' Methods to call
        Ex4Task2_Extensions()
        ... 
    End Sub
    

  2. Add the Ex4Task2_Extensions() method to Program.cs (C#) or Module1.vb (Visual Basic):

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task2_Extensions CSharp)

    C#Copy Code
    private static void Ex4Task2_Extensions()
    {
        var q = employeeData.
            Where(x => x.EmployeeID % 2 == 0).OrderBy(x => x.EmployeeID)
            .Select(x => PayrollServices.GetEmployeeInfo(x))
            .ToList();
    
        foreach (var e in q)
        {
            Console.WriteLine(e);
        }
    }
    

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task2_Extensions VB)

    Visual BasicCopy Code
    Private Sub Ex4Task2_Extensions()
        Dim q = employeeData _
                .Where(Function(x) x.EmployeeID Mod 2 = 0) _
                .OrderBy(Function(x) x.EmployeeID) _
                .Select(Function(x) PayrollServices.GetEmployeeInfo(x)) _
                .ToList()
    
        For Each e In q
            Console.WriteLine(e)
        Next e
    End Sub
    

    Note:
    Again, the ToList() used to execute the LINQ query immediately rather than waiting for it to execute when the IEnumerable<T> returned by Select() is iterated over during the foreach that comes later. You are avoiding Delayed Execution.


  3. Build and run the application.
  4. You should observe that the LINQ query performs the operations in order of the Employee ID. Also observe the total amount of time required to complete the work (the exact time required will vary):

    Figure 15
    Output from non-parallelized LINQ query with extension methods


  5. To parallelize this LINQ query just replace the current method call from Main(), with the following one.
    C#Copy Code
    static void Main(string[] args)
    {
        ...
        // Methods to call
        Ex4Task2_ConvertToParallelExtensions();
        ...
    }
    

    Visual BasicCopy Code
    Sub Main(ByVal args() As String)
        ...
        ' Methods to call
        Ex4Task2_ConvertToParallelExtensions()
        ... 
    End Sub
    

  6. Add the Ex4Task2_ConvertToParallelExtensions() method to Program.cs (C#) or Module1.vb (Visual Basic):

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task2_ConvertToParallelExtensions CSharp)

    C#Copy Code
    private static void Ex4Task2_ConvertToParallelExtensions()
    {
        var q = employeeData.AsParallel()
            .Where(x => x.EmployeeID % 2 == 0).OrderBy(x => x.EmployeeID)
            .Select(x => PayrollServices.GetEmployeeInfo(x))
            .ToList();
    
        foreach (var e in q)
        {
            Console.WriteLine(e);
        }
    }
    

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task2_ConvertToParallelExtensions VB)

    Visual BasicCopy Code
    Private Sub Ex4Task2_ConvertToParallelExtensions()
        Dim q = employeeData.AsParallel() _
                    .Where(Function(x) x.EmployeeID Mod 2 = 0) _
                    .OrderBy(Function(x) x.EmployeeID) _
                    .Select(Function(x) PayrollServices.GetEmployeeInfo(x)) _
                    .ToList()
    
        For Each e In q
            Console.WriteLine(e)
        Next e
    End Sub
    

  7. Build and run the application.
  8. You should observe that like the LINQ query based on the ParallelEnumerable static class, the PLINQ query implemented as extension methods no longer performs operations in order by EmployeeID. Instead the operations are executed in parallel with as many operations occurring concurrently as the number of physical cores will allow. Also note that as in the previous parallelize LINQ example, the parallelized version completes in roughly half the time as the non-parallelized version.

    Figure 16
    Output from parallelized LINQ query with extension methods


Task 3 � Using AsParallel() With Query Comprehension Syntax

In this task you will use the Parallel Extensions library and the AsParallel() method to create parallelized LINQ queries using the query comprehension syntax.

  1. Replace the LINQ query in the Main() method with the following query comprehension syntax:
    C#Copy Code
    static void Main(string[] args)
    {
    
        // Methods to call
        Ex4Task3_PLINQComprehensionSyntax();
    
    }
    

    Visual BasicCopy Code
    Sub Main(ByVal args() As String)
        ...
        ' Methods to call
        Ex4Task3_PLINQComprehensionSyntax()
        ... 
    End Sub
    

  2. Add the Ex4Task3_PLINQComprehensionSyntax() method to Program.cs (C#) or Module1.vb (Visual Basic):

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task3_PLINQComprehensionSyntax CSharp)

    C#Copy Code
    private static void Ex4Task3_PLINQComprehensionSyntax()
    {
        var q = from e in employeeData.AsParallel()
                where e.EmployeeID % 2 == 0
                orderby e.EmployeeID
                select PayrollServices.GetEmployeeInfo(e);
    
        foreach (var e in q)
        {
            Console.WriteLine(e);
        }
    }
    

    (Code Snippet � Intro to Parallel Extensions Lab - Ex4 Ex4Task3_PLINQComprehensionSyntax VB)

    Visual BasicCopy Code
    Private Sub Ex4Task3_PLINQComprehensionSyntax()
        Dim q = From e In employeeData.AsParallel()
                Where e.EmployeeID Mod 2 = 0
                Order By e.EmployeeID
                Select PayrollServices.GetEmployeeInfo(e)
    
        For Each e In q
            Console.WriteLine(e)
        Next e
    End Sub
    

  3. Build and run the application.
  4. You should observe that although the LINQ syntax was changed, the data was processed in the same parallel manner as it was with the ParallelEnumerable extension methods.

    Figure 17
    Output from parallelized LINQ query using the query comprehension syntax


Next Step:

Summary