Tutorial: Using the For Loop from the Parallel Programming Library

From RAD Studio
Jump to: navigation, search

Go Up to Parallel Programming Library Tutorials

This tutorial shows how to use the TParallel.For method from the Parallel Programming Library (PPL). TParallel.For splits a for loop into pieces that run in parallel and uses tasks to run those pieces.

This tutorial consists of a simple application that finds the first X prime numbers. This calculation is done with a serial version using the classic for loop and a parallel version using the TParallel.For method.

Creating the Project

Create a new project:

  • For Delphi, choose File > New > Multi-Device Application - Delphi > Blank Application.
  • For C++Builder, choose File > New > Multi-Device Application - C++Builder > Blank Application.

Adding the Components

  1. Add two TButton components to the form.
    • Set the Name property of one of the buttons to btnForLoop and its Text property to For Loop. This button will launch the serial version of the prime numbers calculation.
    • Set the Name property of the other button to btnParallelLoop and its Text property to Parallel Loop. This button will launch the parallel version of the prime numbers calculation.
  2. Drop a TMemo to the form.
    • Adjust the size of the TMemo to display the messages entirely.

Implementing the TParallel.For Functionality

First, implement the event handlers for the OnClick events of the For Loop and Parallel Loop buttons. The For Loop button runs the function that looks for prime numbers using a classic for loop. The Parallel Loop uses the TParallel.For.

For this, write code as follows:

Delphi:

const
  Max =5000000;
procedure TForm1.btnForLoopClick(Sender: TObject);
var
  I, Tot: Integer;
  SW: TStopwatch;
begin
    // counts the prime numbers below a given value
   Tot:=0;
   SW := TStopWatch.Create;
   SW.Start;
   for I := 1 to Max do
   begin
     if IsPrime(I) then
        Inc(Tot);
   end;
   SW.Stop;
   Memo1.Lines.Add(Format('Sequential For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
end;
procedure TForm1.btnParallelForClick(Sender: TObject);
var
  Tot: Integer;
  SW: TStopwatch;
begin
     try
     // counts the prime numbers below a given value
       Tot :=0;
       SW :=TStopWatch.Create;
       SW.Start;
       TParallel.For(2,1,Max,procedure(I:Int64)
       begin
         if IsPrime(I) then
          TInterlocked.Increment(Tot);
       end);
     SW.Stop;
      Memo1.Lines.Add(Format('Parallel For loop. Time (in milliseconds): %d - Primes found: %d', [SW.ElapsedMilliseconds,Tot]));
     except on E:EAggregateException do
      ShowMessage(E.ToString);
     end;
end;

In Delphi, the function passes to the TParallel.For as an anonymous method.

You should also add the implementation code of the IsPrime function:

function IsPrime(N: Integer): Boolean;
var
  Test, k: Integer;
begin
  if N <= 3 then
    IsPrime := N > 1
  else if ((N mod 2) = 0) or ((N mod 3) = 0) then
    IsPrime := False
  else
  begin
    IsPrime := True;
    k := Trunc(Sqrt(N));
    Test := 5;
    while Test <= k do
    begin
      if ((N mod Test) = 0) or ((N mod (Test + 2)) = 0) then
      begin
        IsPrime := False;
        break; { jump out of the for loop }
      end;
      Test := Test + 6;
    end;
  end;
end;

C++:

void __fastcall TForm1::btnForLoopClick(TObject *Sender)
{
	  int Max=5000000;
	  Tot =0;
	  System::Diagnostics::TStopwatch sw= System::Diagnostics::TStopwatch::Create();
	  sw.Start();
	  for(int I=1;I<=Max;I++){
		if(IsPrime(I)) {
				Tot++;
		}
	  }
	  sw.Stop();
	  Memo1-> Lines->Add(
		String().sprintf(L"Sequential For loop. Time (in milliseconds): %lld, Primes found: %lld",sw.ElapsedMilliseconds, Tot));

}
void __fastcall TForm1::btnParallelForClick(TObject *Sender)
{
	  int Max=5000000;
	  Tot =0;
	  System::Diagnostics::TStopwatch sw= System::Diagnostics::TStopwatch::Create();
	  sw.Start();
	  TParallel::For(NULL,1,Max,MyIteratorEvent);
	  sw.Stop();
	   Memo1-> Lines->Add(
		String().sprintf(L"Parallel For loop. Time (in milliseconds): %lld, Primes found: %lld",sw.ElapsedMilliseconds, Tot));

}

In C++, the function passes to the TParallel.For as an Iterator Event.

The Iterator Event implementation code is as follows:

void __fastcall TForm1::MyIteratorEvent(TObject* Sender, int AIndex)
{
	if(IsPrime(AIndex)){
		TInterlocked::Increment(Tot);
	}
}

The code for the IsPrime function in C++ is:

bool TForm1::IsPrime(int N){
	int Test, k;
	bool aPrime;
	if (N <= 3){
	   return N > 1;
	}
	else if (((N % 2) == 0) || ((N % 3) == 0)){
		return false;
	}else{
		aPrime = true;
		k = (int)sqrt(N);
		Test = 5;
		while (Test <= k){
			if (((N % Test) == 0) || ((N % (Test + 2)) == 0)){
				aPrime = false;
				break; //jump out of the for loop
			}
			Test = Test + 6;
		}
		return aPrime;
	}
}

Add also the following declarations to the project header file:

private:	// User declarations
	void __fastcall MyIteratorEvent(TObject* Sender, int AIndex);
public:     // User declarations
       int Tot;

You should also include the necessary libraries to the code.

Delphi:

For Delphi applications, add the following unit to the uses clause if they are not present:

uses
  System.Threading;
  System.Diagnostics;
  System.SyncObjs;

C++:

For C++ applications, add the following include operator to the project header file:

#include <System.Threading.hpp>
#include <System.Diagnostics.hpp>
#include <System.SyncObjs.hpp>

Running the Application

At this point, you can now run the application:

  • Press F9 or choose Run > Run.
    Forlooptutorial.png

You can see how the TParallel.For is more efficient since it makes use of the available CPUs for running procedures in parallel.

See Also