Multithreaded Sorting with C++Builder and the Parallel Programming Library (PPL)

The Parallel Programming Library (PPL) is one of my favorite features in C++Builder runtime library. PPL allows developers to create tasks that run in parallel to take advantage of multi-core processors.

Using the PPL, you can: 1) speed up loops with a Parallel For, 2) run multiple tasks in parallel using TTask, and 3) use Future Objects to allow a process run with your program focused on other work until the future value is set.

To showcase the TTask feature of the PPL, I’ve created a C++Builder VCL application (build and tested using the C++Builder 10.4 Sydney release) that runs three sort algorithms in separate tasks – Bubble Sort, Shell Sort and the ISO C++ standard Sort (which implements the Quicksort algorithm).

The User Interface

The VCL user interface for my application includes a TButton, two TMemos, and four TLabels. The TButton onClick event handler creates a vector of integers, creates three TTasks (for the sort algorithms) and waits for the sort tasks to complete using the TTask::WaitForAll method.

The Code

MainUnit.h:

#ifndef MainUnitH
#define MainUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
	TButton *Button1;
	TLabel *SortingStatusLabel;
	TLabel *BubbleSortLabel;
	TMemo *Data_Memo;
	TMemo *SortResult_Memo;
	TLabel *ShellSortLabel;
	TLabel *STDSortLabel;
	void __fastcall Button1Click(TObject *Sender);
private:	// User declarations
public:		// User declarations
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

MainUnit.cpp:

//---------------------------------------------------------------------------

#include <vcl.h>
#include <System.Threading.hpp>
#include <vector>
#include <algorithm>
#pragma hdrstop

#include "MainUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

	_di_ITask My_Tasks[3];
	const int max_data = 15000;   // number of random numbers to create

	int LoopValue = 0;

	Button1->Enabled = false;
	Button1->Update();
	BubbleSortLabel->Caption = "Bubble Sort";
	BubbleSortLabel->Update();
	ShellSortLabel->Caption = "Shell Sort";
	ShellSortLabel->Update();
	STDSortLabel->Caption = "std::sort";
	STDSortLabel->Update();

	SortingStatusLabel->Caption = "Sorting "+IntToStr(max_data)+" integers!";
	SortingStatusLabel->Update();

	// Windows GetTicks() start and stop for each sort method
	int StartBubble,StopBubble;
	int StartShell,StopShell;
	int StartSTD,StopSTD;

	Data_Memo->Lines->Clear();
	SortResult_Memo->Lines->Clear();

	// create a vector of data that all sort algorithms will use
	std::vector<int> my_data;

	// populate the vector with integers
	for (int i = 1; i <= max_data; i++) {
		int random_value = Random(max_data);
		my_data.push_back(random_value);
		// Data_Memo->Lines->Add(IntToStr(random_value));
	}

	// copy data vector to sort vectors
	std::vector<int> bubble_data = my_data;
	std::vector<int> shell_data = my_data;
	std::vector<int> std_data = my_data;

	// First Task - Bubble Sort
	My_Tasks[0] = TTask::Create([&](){
		// Set Bubble Sort Windows GetTickCount()
		StartBubble = GetTickCount();
		// body of Bubble Sort
		bool swapp = true;
		while(swapp){
			swapp = false;
			for (size_t i = 0; i < bubble_data.size()-1; i++) {
				if (bubble_data[i]>bubble_data[i+1] ){
					bubble_data[i] += bubble_data[i+1];
					bubble_data[i+1] = bubble_data[i] - bubble_data[i+1];
					bubble_data[i] -=bubble_data[i+1];
					swapp = true;
				}
			}
		}
		// Set the Bubble Sort Stop GetTicks()
		StopBubble = GetTickCount();
	});
	// Start the Bubble Sort Task
	My_Tasks[0]->Start();


	// Second Task - Shell Sort
	My_Tasks[1] = TTask::Create([&](){
		// Set Shell Sort Windows GetTickCount()
		StartShell = GetTickCount();
		// body of the Shell Sort
		for (int gapSize = shell_data.size() / 2; gapSize > 0; gapSize /= 2) {
			for (int currentIndex = gapSize; currentIndex < shell_data.size(); currentIndex++) {
				// save the currentIndex
				int currentIndexCopy = currentIndex;
				// save the value of the currentIndex
				int item = shell_data[currentIndex];
				while (currentIndexCopy >= gapSize && shell_data[currentIndexCopy - gapSize] > item) {
					shell_data[currentIndexCopy] = shell_data[currentIndexCopy - gapSize];
					currentIndexCopy -= gapSize;
				}
				shell_data[currentIndexCopy] = item;
			}
		}
		// Set the Shell Sort Stop GetTicks()
		StopShell = GetTickCount();
	});
	// Start the Shell Sort
	My_Tasks[1]->Start();


	// Third Task - std::sort
	My_Tasks[2] = TTask::Create([&](){
		// Set std::sort Windows GetTickCount()
		StartSTD = GetTickCount();
		// Body of the std::sort
		std::sort(std_data.begin(),std_data.end());
		// Set the std::sort Stop GetTicks()
		StopSTD = GetTickCount();
	});
	// Start the Shell Sort
	My_Tasks[2]->Start();

	// wait until all of the sorting tasks complete
	TTask::WaitForAll(My_Tasks, sizeof(My_Tasks)/sizeof(My_Tasks[0])-1);

	SortingStatusLabel->Caption = "Sorting All done!";

	BubbleSortLabel->Caption = "Bubble Sort Time: "
		+ IntToStr(StopBubble - StartBubble)
		+ " ms";
	BubbleSortLabel->Update();

	ShellSortLabel->Caption = "Shell Sort Time: "
		+ IntToStr(StopShell - StartShell)
		+ " ms";
	ShellSortLabel->Update();

	STDSortLabel->Caption = "std::sort: "
		+ IntToStr(StopSTD - StartSTD)
		+ " ms";
	STDSortLabel->Update();

	// if you want to see the sort results un-comment
	// one of the for loops and the sort result memo statement
	// for(int n : std_data) {
	// for(int n : bubble_data) {
	// for(int n : shell_data) {
		// SortResult_Memo->Lines->Add(IntToStr(n));
	// }

	// clear the vectors
	my_data.clear();
	bubble_data.clear();
	shell_data.clear();
	std_data.clear();

	Button1->Enabled = true;
}
//---------------------------------------------------------------------------

The form after sorting 15,000 integers

References

PPL – Parallel Programming Library – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_the_Parallel_Programming_Library

Using TTask – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TTask_from_the_Parallel_Programming_Library

Using TParallel::For – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TParallel.For_from_the_Parallel_Programming_Library

Using TTask::IFuture – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TTask.IFuture_from_the_Parallel_Programming_Library

Algorithms Library – https://en.cppreference.com/w/cpp/algorithm

std::sort – https://en.cppreference.com/w/cpp/algorithm/sort

C++Builder Product Information

C++Builder Product Page – Native Apps that Perform. Build Windows C++ Apps 10x Faster with Less Code
C++Builder Product Editions – C++Builder is available in four editions – Professional, Enterprise, Architect and Community (free). C++Builder is also available as part of the RAD Studio development suite.