Share via


Exercise 1: Parallelize an Existing Algorithm using the Static Parallel Helper Class

In this exercise, you will learn how to parallelize and existing algorithm by using the static Parallel helper class. This allows you to do things like replacing for() with Parallel.For().

Note:
To verify that each step is correctly performed, it is recommended to build the solution at the end of each task.

Task 1 – Parallelizing a Long Running Service

In this task, you will write some simple sample code that will simulate a long running service call.

You are going to use the PayrollServices.GetPayrollDeduction() method which is provided with the begin solution of this exercise. This is the type of long running code that you would ultimately like to run in parallel.

  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 underSource\Ex01-UsingStaticParallelHelper\begin. (Choosing the folder that matches the language of your preference.)

    Note:
    This solution contains a starting point for your work, and includes a helper class EmployeeList which holds the data you’ll be working with.

  3. In Visual Studio, open the Program.cs (C#) or Module1.vb (Visual Basic) file and navigate to its Main() method. First, you will need to create a list of employees to work on, so add a class variable and initialize it in the Main() method:

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Create employee list CSharp)

    C#

    class Program
    FakePre-cdbfc5439ac14cb5aef9facca87d6187-01bfe417ec9d4e5fb983215540f79ae2 private static EmployeeList employeeData; static void Main(string[] args) { employeeData = new EmployeeList(); Console.WriteLine("Payroll process started at {0}", DateTime.Now); var sw = Stopwatch.StartNew(); // Methods to call Console.WriteLine("Payroll finished at {0} and took {1}", DateTime.Now, sw.Elapsed.TotalSeconds); Console.WriteLine(); Console.ReadLine(); }FakePre-76e9d5402e9c4028a0c13436a1207b3a-d5140b8f04ea451aa9c5b0de1136943fFakePre-608c1af0ba7b4ec6abbdf4a446a0ec5b-5681fcd93c1a45ab8994f9a93c6e22f7FakePre-3a44c301917643ab9a296a30a47c210d-af35ab44cd0946aea77cb7fb187423e7FakePre-7d474c0cb0734a8cb2e30dc4584a3dd0-f13e3887acff437c940418f612c82d8e

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Create employee list VB)

    Visual Basic

    Module Module1
    Private employeeData As EmployeeList Sub Main(ByVal args() As String) employeeData = New EmployeeList() Console.WriteLine("Payroll process started at {0}", DateTime.Now) Dim sw = Stopwatch.StartNew() ' Methods to call Console.WriteLine("Payroll finished at {0} and took {1}", DateTime.Now, sw.Elapsed.TotalSeconds) Console.WriteLine() Console.ReadLine() End SubFakePre-32f5696b37484880ab47f72db0aac75e-d761444f6f9142548438964d4b1d482aFakePre-693cd792db04411bac04cf755dc1edca-a1994b14851e487694652748a8938c78FakePre-d6c115373e874e34927e1038583787cf-704bef2ccf5543729fc1846703bb4dccFakePre-e74aa1214a9047999889ed43018c2cbd-8b206fe728104a7fb3bf9c50ef4b4490FakePre-82ec6cf3621e4e1ab2038c45d43523ca-70450c45102b407d95570bfa3e622935

  4. Now add the following method to Program.cs (C#) or Module1.vb (Visual Basic). This method will use a standard for loop to iterate through a list of Employees, as provided by the pre-built code and call the long-running PayrollServices.GetPayrollDeduction() method. The code should look like:

    (Code Snippet – Intro to Parallel Extensions Lab –Ex1 Ex1Task1_ParallelizeLongRunningService CSharp)

    C#

    private static void Ex1Task1_ParallelizeLongRunningService() { Console.WriteLine("Non-parallelized for loop"); for (int i = 0; i < employeeData.Count; i++) { Console.WriteLine("Starting process for employee id {0}", employeeData[i].EmployeeID); decimal span = PayrollServices.GetPayrollDeduction(employeeData[i]); Console.WriteLine("Completed process for employee id {0}" + "process took {1} seconds", employeeData[i].EmployeeID, span); Console.WriteLine(); } }

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_ParallelizeLongRunningService Visual Basic)

    Visual Basic

    Private Sub Ex1Task1_ParallelizeLongRunningService() Console.WriteLine("Non-parallelized for loop") For i = 0 To employeeData.Count - 1 Console.WriteLine("Starting process for employee id {0}", employeeData(i).EmployeeID) Dim span As Decimal = PayrollServices.GetPayrollDeduction(employeeData(i)) Console.WriteLine("Completed process for employee id {0}" & "process took {1} seconds", employeeData(i).EmployeeID, span) Console.WriteLine() Next i End Sub

  5. Call the method Ex1Task1_ParallelizeLongRunningService from Main().

    C#

    static void Main(string[] args)
    FakePre-a68d1533ad274131be6c4a77eae0016f-52e57ee96b934239b55edfc0603e262fFakePre-091af2fb84ec41fc9cc4feee4602d7bc-6b2114e67e35443992567c5ddfaa60d9FakePre-5e39342e10b04c95be43f83c712155da-c59168b86ad547249e98c5615656cb87 Ex1Task1_ParallelizeLongRunningService();FakePre-18cb428fd5b940dbade88895f7f33e73-067fe157d72e442eb350c6e85ba393ffFakePre-ddfa83065b944b6785f0efb976012b2f-f2a22091d151407fa4d0f892293c18d0

    Visual Basic

    Sub Main(ByVal args() As String)
    FakePre-2468c5efd875487285ec9dde7fdeab37-632d39e6085147eab85eecfb7a6732d0FakePre-199a9373140e4e05a6011900e41e77d3-6fbc5d68fb7847aba284d187bf3b5562 Ex1Task1_ParallelizeLongRunningService()FakePre-501ca2fac64548f0bca5651c794ccac5-b066b8b023c64802ba8a55b9ae4bf286FakePre-82b66b47556744f69bd293a035a547ad-fddbf6c03f054cb3bd3c543ce937f8c9

  6. Build and run the application.
  7. You should see that the employees are all processed in order of their IDs, similar to the following (the exact time to complete will vary):

    Figure 1

    Output from non-parallel calls to a long running service

  8. To work with the parallelization features, add the following method to Program.cs (C#) or Module1.vb (Visual Basic). This code uses the For() method from the static Parallel object:

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_UseParallelForMethod CSharp)

    C#

    private static void Ex1Task1_UseParallelForMethod() { Parallel.For(0, employeeData.Count, i => { Console.WriteLine("Starting process for employee id {0}", employeeData[i].EmployeeID); decimal span = PayrollServices.GetPayrollDeduction(employeeData[i]); Console.WriteLine("Completed process for employee id {0}", employeeData[i].EmployeeID); Console.WriteLine(); }); }

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_UseParallelForMethod VB)

    Visual Basic

    Private Sub Ex1Task1_UseParallelForMethod() Parallel.For(0, employeeData.Count, Sub(i) Console.WriteLine("Starting process for employee id {0}", employeeData(i).EmployeeID) Dim span As Decimal = PayrollServices.GetPayrollDeduction(employeeData(i)) Console.WriteLine("Completed process for employee id {0}", employeeData(i).EmployeeID) Console.WriteLine() End Sub) End Sub

  9. Replace the current method calls from Main() with a call to Ex1Task1_UseParallelForMethod() method.

    C#

    static void Main(string[] args)
    FakePre-4c370a6940ff4625809e654b192090fa-f7919e045d264717bc730dfa7008be0cFakePre-498098cda4004f0e96f652303fcff4ed-a9c055b477634cd3ab93fdfce1efddddFakePre-9680393baf77425e8cfbf75b5e247524-f6c7d7556cbb431bb27fe6c3827942ca Ex1Task1_UseParallelForMethod();FakePre-f2e545ef017444c8bf77a48aeb4ba374-64f3ceed5944484fadaa835aa67f2626FakePre-d440776501424930865875d574351660-68916e934a3341f48b6bbe0d30d3878e

    Visual Basic

    Sub Main(ByVal args() As String)
    FakePre-5621f14acd9546e388fc0b3e6b9ab8ce-13422571bbad4fd6a7ac47ceac34b872FakePre-344a768fba77415cbee0595c7ec0d4b5-5710f78dc1c94e1297ebfee178935d24 Ex1Task1_UseParallelForMethod()FakePre-c8f40364160947fbbd2c232ef6de9497-7c00bac91257481795cb2c71f7d4954fFakePre-fd15288e69da41218557825e8c86a6ac-9182ba46a77b47d98caa5a738cdb3af1

  10. Build and run the application.
  11. You should observe that the employees are not necessarily processed in the order of their IDs. You’ll also notice that multiple calls to the GetPayrollDeduction() method are made before the first call returns. And finally, you should observe that by running the calls in parallel, the entire job completed much faster than when run in serial.

    Figure 2

    Output from parallel calls to a long running service

    Note:
    Because the loop is run in parallel, each iteration is scheduled and run individually on whatever core is available. This means that the list is not necessarily processed in order, which can drastically affect your code. You should design your code in a way that each iteration of the loop is completely independent from the others. Any single iteration should not rely on another in order to complete correctly.

  12. The Parallel Extensions library also provides a parallel version of the foreach structure. The following code demonstrates the non-parallel way to implement this structure. Add the following method to Program.cs (C#) or Module1.vb (Visual Basic).

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_StandardForEach CSharp)

    C#

    private static void Ex1Task1_StandardForEach() { foreach (Employee employee in employeeData) { Console.WriteLine("Starting process for employee id {0}", employee.EmployeeID); decimal span = PayrollServices.GetPayrollDeduction(employee); Console.WriteLine("Completed process for employee id {0}", employee.EmployeeID); Console.WriteLine(); } }

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_StandardForEach VB)

    Visual Basic

    Private Sub Ex1Task1_StandardForEach() For Each employee As Employee In employeeData Console.WriteLine("Starting process for employee id {0}", employee.EmployeeID) Dim span As Decimal = PayrollServices.GetPayrollDeduction(employee) Console.WriteLine("Completed process for employee id {0}", employee.EmployeeID) Console.WriteLine() Next employee End Sub

  13. In the Main() method, replace the Parallel.For(…) loop with the following code:

    C#

    static void Main(string[] args)
    FakePre-3c42a2ab9767408597842173b8164176-f60c0a0814e64214b773d4be9b99e38bFakePre-e95069332db245099287396982756c48-b306de88c7e44164a2f54f16f14a4024FakePre-5e2fb302333649b0b7293519fc3a1b29-c8fc3992e3cc4f8f9d4048382f076341 Ex1Task1_StandardForEach();FakePre-b11fda32bc4e4ee89588548097c9cf0c-371c3c22af5143bb9927f3465248128fFakePre-ccc452657a804813a798a093c512f79c-c1b26eeed4474eaa9ca56406e675f4a1

    Visual Basic

    Sub Main(ByVal args() As String)
    FakePre-25866d96175947c7ab9d7c2460580e9a-5e3dc43ba7294beebd685cca8c2e5b0cFakePre-8eb4da6b79894dddbbc1b2e38d409479-4f0aff3bdef14fedafb65abd53f1432a Ex1Task1_StandardForEach()FakePre-972db12de533466488d4cfa8e8f878b0-83825ee9f25f49a9ab577b942526eeacFakePre-a27256c79697439596ef19f71f3177a6-975d6f6dd56f4b6585b050c6d85f2296

  14. Build and run the application.

    Note:
    You should observe that the employees are once again processed in the order of the IDs. Also take note of the total amount of time required to complete this job (the exact time required will vary)

    Figure 3

    Output from non-parallel for…each implementation

  15. To utilize the Parallel Extensions implementation of the for…each structure you’ll need to change the code to use the ForEach() method. In Program.cs (C#) or Module1.vb (Visual Basic)add the following method:

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_ParallelForEach CSharp)

    C#

    private static void Ex1Task1_ParallelForEach() { Parallel.ForEach(employeeData, ed => { Console.WriteLine("Starting process for employee id {0}", ed.EmployeeID); decimal span = PayrollServices.GetPayrollDeduction(ed); Console.WriteLine("Completed process for employee id {0}", ed.EmployeeID); Console.WriteLine(); }); }

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_ParallelForEach VB)

    Visual Basic

    Private Sub Ex1Task1_ParallelForEach() Parallel.ForEach(employeeData, Sub(ed) Console.WriteLine("Starting process for employee id {0}", ed.EmployeeID) Dim span As Decimal = PayrollServices.GetPayrollDeduction(ed) Console.WriteLine("Completed process for employee id {0}", ed.EmployeeID) Console.WriteLine() End Sub) End Sub

  16. Replace the current method calls from Main(), with a call to Ex1Task1_ParallelForEach method.

    C#

    static void Main(string[] args)
    FakePre-4cab76dd3aed4769b30d6ec98a73547d-7a2746a4174e47e78889735a0df58d9aFakePre-90d8d268f8464bdf93092c8d4c491d4d-99e5f6dc13794b7f823fdfd3f5fb7918FakePre-dbf7e31c0b8348e4b91ce9fa196a2176-e13c4660cd0f469693e0924a9bdf35ea Ex1Task1_ParallelForEach();FakePre-c185d02c56bd4dc9b8e3fd38ad329a9d-0d7bc73fea0e4f4fa6e4e2e403a0c22aFakePre-7d1671ebd95d4a2c8f19428bf77f264b-7faf57ed8fa240d79fe1a872140ed04e

    Visual Basic

    Sub Main(ByVal args() As String)
    FakePre-5308523580bf47cb8c14ed600458dacc-83d21aa5ea03421987b12b849e9b7e5aFakePre-3517169877ef4274b42e3a61cb32ed37-4a0e244a846c4a3390ac5d4bb12ca890 Ex1Task1_ParallelForEach()FakePre-771d020ab96d4d6d8a35128fec046abc-c873519ecdc743ac98b8d5ed900ee062FakePre-8acde6c7fd5844ecae8639788161c894-f021078bf0e647dda8b165c728d11bf5

  17. Build and run the application.
  18. You will again observe that the employees are not necessarily processed in order of their ID and because each loop is run in parallel, each iteration of the loop is run individually on whatever core is available. Also, since the application is utilizing all available cores the job is able to complete faster than when run in a serial manner.

    Figure 4

    Output from parallel for…each implementation

    Note:
    The Parallel Extensions Library also provides a useful Invoke() method, that allows parallel execution of anonymous methods or lambda expressions. To help illustrate how to use the Invoke method you will examine a common tree-walking algorithm and then see how it can be parallelized to reduce the total time needed to walk the entire tree.

    In this example you will walk an employee hierarchy and call the GetPayrollDeduction() method for each employee you encounter.

  19. Replace the current method calls from Main(), with a call to Ex1Task1_WalkTree() method. This code instantiate the employee hierarchy and call the tree walker method.

    C#

    static void Main(string[] args)
    FakePre-c12df893c4c24ebca91ca23667c2a77c-87bcc9326ec44b4388de34a530bfe896FakePre-967cd524dc094c3ea6303a37dbea7f04-2ecbd95d6ce04d4fbc1d77a57cf42563FakePre-7f7ee68ed0d647359b00e6589adc0c0d-bcef9caf534d4824823ba0149fe9a116 Ex1Task1_WalkTree();FakePre-be5b06404c384eae9fe00c6576c7020b-95bcc75b33c543628195123ae25040edFakePre-3c3cd52acb1d43fe91525753966e2211-b738a4fe376a4427b6091fd4a58aedaf

    Visual Basic

    Sub Main(ByVal args() As String)
    FakePre-4daba2bc1c874c0596592907584b94bd-2c99792119d74afc96a184f71c45b585FakePre-17113c82702a45eebbcf9166e82419e6-93393f84c7f04eb087b5c06b75631da1 Ex1Task1_WalkTree()FakePre-438a580ade19453ba4e65e6317a7fc67-d2db199ec41f43a7b6513661fdf6b1f7FakePre-da51403cb5064c2dbd7b270e6f9ad20f-6dce5357c18f4e80b5324653a40bcd77

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_WalkTree CSharp)

    C#

    private static void Ex1Task1_WalkTree() { EmployeeHierarchy employeeHierarchy = new EmployeeHierarchy(); WalkTree(employeeHierarchy); }

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 Ex1Task1_WalkTree VB)

    Visual Basic

    Private Sub Ex1Task1_WalkTree() Dim employeeHierarchy As New EmployeeHierarchy() WalkTree(employeeHierarchy) End Sub

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

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 WalkTree CSharp)

    C#

    private static void WalkTree(Tree<Employee> node) { if (node == null) return; if (node.Data != null) { Employee emp = node.Data; Console.WriteLine("Starting process for employee id {0}", emp.EmployeeID); decimal span = PayrollServices.GetPayrollDeduction(emp); Console.WriteLine("Completed process for employee id {0}", emp.EmployeeID); Console.WriteLine(); } WalkTree(node.Left); WalkTree(node.Right); }
    FakePre-94d289284b46410c8615092e87d5713d-40e254a726964345965bf91924a499d3
    

    (Code Snippet – Intro to Parallel Extensions Lab - Ex1 WalkTree VB)

    Visual Basic

    Private Sub WalkTree(ByVal node As Tree(Of Employee)) If node Is Nothing Then Return End If If node.Data IsNot Nothing Then Dim emp As Employee = node.Data Console.WriteLine("Starting process for employee id {0}", emp.EmployeeID) Dim span As Decimal = PayrollServices.GetPayrollDeduction(emp) Console.WriteLine("Completed process for employee id {0}", emp.EmployeeID) Console.WriteLine() End If WalkTree(node.Left) WalkTree(node.Right) End Sub
    FakePre-b6b3001538dd42ab86153c821a7d5672-3ddce33acd1a4182ae37694d79c1ca4f
    

  21. Build and run the application.
  22. You should observe the employees are processed in order of their IDs. Also note the total amount of time required to walk the tree (the exact time required will vary):

    Figure 5

    Output from a non-parallel tree walker

    Note:
    The tree has been structured so that the data will be written out in ID order when the tree is walked using the non-parallel algorithm provided above.

  23. To walk the tree in a parallel manner remove the two calls to WalkTree() at the end of the WalkTree() method and replace them with a call to the Invoke() Method of the static Parallel class:

    C#

    private static void WalkTree(Tree<Employee> node)
    FakePre-b302b56e01574bea9bcf16a861124324-70367951ce3547ec93123553a16e3279FakePre-c4804f4d9a8f40b48a4564cb2633997a-cfcf1f5ccc014810b073ff81057054bbFakePre-f7b8df4d090a40b8a749dd2c877f617d-e2ae68f2164c4b31948d001acdda8664FakePre-e58f31afed99419db4139ec3db96c3c1-4d0cad4bd57d4f4090a7c2c87d55389dFakePre-3005d49372604911b3ec7950cc802709-820ad8ededb54a9b80292263ab57361fFakePre-6364ebc257ad4db4ac2e9bda70724c63-660c9886f9d3474b9799d01eccebf8cbFakePre-38918b2e5bcc4e98813a40dfc8e5a30b-d35679e7ab424557afefec7a703ee112FakePre-4c5aaed7852b467d98db89c4fd4ec9f5-a6a5b7816ede457186d50c2e92276793FakePre-b46229aff04b4da495d7cc628d1781b1-b9d0d33a40d74ae18b260ac8f9d066d0FakePre-4438e06102db46e7b9aa8bb9ea8f9e12-c14358c61c7d4f749ee13884b1513414FakePre-39532a26f7f04975b413f333fea687a5-a396531ac8064ef8800659e1bd499088FakePre-892a13094e3a4c45b153c4602096038d-9d59be948c114de7adebfd1c75faa3c1FakePre-10974662bef34b0e8d995c764b8dbc77-db726fabf8be4c839773fcde43cc4a19FakePre-7ba104aaa7d249329da04377893b2f2f-bb249cbf59a74ccd83c64db2538be020FakePre-95cf246247d644e4b7f0863a22b4fe63-d1a72128df0641c0b49dbe0313125a63 Parallel.Invoke(delegate { WalkTree(node.Left); }, delegate { WalkTree(node.Right); });FakePre-524bc8120ae84af99afb2982b1c0ce1b-f885b3f97bc9444ba7a9457697170418

    Visual Basic

    Private Sub WalkTree(ByVal node As Tree(Of Employee))
    FakePre-b9ec8a8defd84647846d8ff1a65894b7-29451c6a45d74aa59d45aab7376bfe00FakePre-2aba114ce0214df3b6d6dacd9c181ad2-07993c44f99b4196b35f81cfa260e063FakePre-4e183ec7cf05449bac522ec9dc600669-6664a99f744a4efd9dd942986ab07f39FakePre-35cad6ab15504f4e877920196cb5c481-0a5e5f5e10e14ae08b8d6a0aaadb98e2FakePre-0eba472bf09944ceb82cd2fc9f31db52-1107657a08ef4dc7badbc52d55c6cb54FakePre-9a9bda075d89401d8c0b246bcd464e26-7f8ac22fd8864a3ead201ade31917788FakePre-77f3f04765ec408eba0c26336566df38-013120eeb6544dd2b81d423d43511f27FakePre-9f600817aba3444ea8f7d03259aa4b4c-c08ffcd0aa3442e3b6690434b720eb63FakePre-903fd90473d64f52af909b82c73be38e-bce464baa0e84ed8ae9ca1cae66aaaedFakePre-137278e772844dcf86a2f7eb78debc52-bbb060bd627e4a8dbebfd2ecef1e2e2bFakePre-fec5adfabc804af6b81326d4311d1308-1418def0650e48b6910682fef32523e7FakePre-f2169c85b8b54290897ade620dfda89a-c2422e658c0646f094e5597d93ce4274Parallel.Invoke(Sub() WalkTree(node.Left), Sub() WalkTree(node.Right))FakePre-ff937e9b8a3c4d83971742d940799a74-fc2b4d464eb7469f829fff07614a8b7b

  24. Build and run the application.
  25. You should observe that the employees in the tree are no longer processed in the same order and that several nodes start processing before others have completed. Also note that it took less time to walk the entire tree.

    Figure 6

    Output from a parallel tree walker

    Note:
    The Invoke() method schedules each call to WalkTree() individually, based on core availability. This means that the tree will not necessarily be walked in a predictable manner. Again, keep this in mind as you design your code.

Next Step:

Exercise 2: Create and Run Parallelized Tasks