20
Jan
0

[C/C++] OpenMP vs PThread – openmp or posix thread ?

A popular question. Which one to use, which one is faster. Well, I do not really know but this post may help you to make your choice.

Introduction

Many people want to know which “concept” is better. Depending on your knowledge about programming you already know what I will explain here, but you can look to the example at the bottom of this page anyway.

PThread

Wiki says:

POSIX Threads, or Pthreads, is a POSIX standard for threads. The standard, POSIX.1c, Threads extensions (IEEE Std 1003.1c-1995), defines an API for creating and manipulating threads.

PThread is a standard. So if you are on a “posix-OS” you can find it as a library. In a non-posix OS (like windows) you can find a library that respects the standard (if someone has made it).
To use a posix thread you have to call a function and passing a “callback” in parameter.

#include <pthread.h>
void * callback (void *argument)
{
return NULL;
}

pthread_t thread;
pthread_create(&thread, NULL, callback, 0);

Usually, we use this kind of thread when we want to do several tasks in parallel (“yeah this is the definition of thread” – you are right but!) which is different from doing a task (and only one) in parallel. So usually with posix thread we are not concern about efficiency and performance, and we are not concerned by the number or cpu on the pc. A typical use of pthread is this one (a graphical interface, with a thread that is doing some job in back ground and another thread that is waiting for the network communication):

posix thread typical use

OpenMP

Wiki says :

OpenMP (Open Multi-Processing) is an application programming interface (API) that supports multi-platform shared memory multiprocessing programming in C, C++, and Fortran on many architectures, including Unix and Microsoft Windows platforms. It consists of a set of compiler directives, library routines, and environment variables that influence run-time behavior.

OpenMP is a specification, so if your compiler says you can use OpenMP then it means that your compiler respects the implementation: it is portable!
OpenMP is made to reduce the lines of code needed to use thread. Also, it is very easy to use especially for loop or stuff like that.

int main(int argc, char *argv[]) {
const int N = 100000;
int i, a[N];

#pragma omp parallel for
for (i = 0; i < N; i++)
a[i] = 2 * i;

return 0;
}

In the previous example the code will be automatically parallelized (depending on the number of cpu where your code is processing). But in contrary, it is very difficult to use OpenMP to make one thread managing UI, another working in the back and another managing network – with Qt framework for example.

openmp typical use

Test

I did a very basic and fast example to know which one is faster & efficient: to allocate, etc. To show that the time needed by the PThread to use a callback or truncating the thread-id is null I did several version.

#include <iostream>
#include <omp.h>

/**
* From http://berenger.eu (contact at berenger dot eu)
* In this post we compare the OpenMp and the Posix thread (pthread)
* in their implementation (how to use)
* and in their efficiency (with a naive example).
*
* As you may now OpenMP is related to compiler
* and pthread is a library
*
* Both can be seen as standard library (Pthread exists on Windows !)
*/

// compile on linux : g++  main.cpp -o main -fopenmp -lpthread -Wall

/** The number of loop to test */
const long NbLaps = 1000000;
/** The number of thread to create a each loop */
const int NbThreads = 4;

/**
* The more basic openmp function we can do
*/
void TestOpenmp(){
	std::cout << "------- Test Openmp -------\n";

	const double startTime = omp_get_wtime();

	for(long index = 0 ; index < NbLaps ; ++index ){
		#pragma omp parallel num_threads(NbThreads)
		{
		}
	}

	const double endTime = omp_get_wtime();

	std::cout << "Duration = " << (endTime - startTime) << std::endl;
}

/**
* Because we usually need the id let use it
* We will also do the same with pthread
*/
void TestOpenmpWithId(){
	std::cout << "------- Test Openmp With Id -------\n";

	const double startTime = omp_get_wtime();

	for(long index = 0 ; index < NbLaps ; ++index ){
		#pragma omp parallel num_threads(NbThreads)
		{
			const int id = omp_get_thread_num();
		}
	}

	const double endTime = omp_get_wtime();

	std::cout << "Duration = " << (endTime - startTime) << std::endl;
}

/**
* Because pthread need a callback, we simulate this callback for openmp
*/
void OpenmpFunction(const int id){
}

/**
* Each thread call the callback function with is own id
*/
void TestOpenmpWithIdAndFunction(){
	std::cout << "------- Test Openmp With Id And Function-------\n";

	const double startTime = omp_get_wtime();

	for(long index = 0 ; index < NbLaps ; ++index ){
		#pragma omp parallel num_threads(NbThreads)
		{
			OpenmpFunction(omp_get_thread_num());
		}
	}

	const double endTime = omp_get_wtime();

	std::cout << "Duration = " << (endTime - startTime) << std::endl;
}

/**
* The pthread id withou using the parameter
*/
void* PthreadBasic(void *)
{
	return 0;
}
/**
* The more basic pthread function
* Please take in consideration that we must wait each thread
* to avoid to much thread in memory (openmp does the same alone)
*/
void TestPthreadFunction(){
	std::cout << "------- Test Pthread Function-------\n";

	const double startTime = omp_get_wtime();

	pthread_t threads[NbThreads];
	for(long index = 0 ; index < NbLaps ; ++index ){
		for(long indexThread = 1 ; indexThread < NbThreads ; ++indexThread){
			pthread_t thread;
			pthread_create(&threads[indexThread], 0, PthreadBasic, 0);
		}
		PthreadBasic(0);
		for(long indexThread = 1 ; indexThread < NbThreads ; ++indexThread){
			pthread_join(threads[indexThread], 0);
		}
	}

	const double endTime = omp_get_wtime();

	std::cout << "Duration = " << (endTime - startTime) << std::endl;
}

/**
* Pthread callback with id
*/
void* PthreadWithId(void * vId)
{
	const long id = (int) vId;
	return 0;
}

/**
* As previously but we also give the id in parameter
*/
void TestPthreadFunctionWithId(){
	std::cout << "------- Test Pthread Function With Id-------\n";

	const double startTime = omp_get_wtime();

	pthread_t threads[NbThreads];
	for(long index = 0 ; index < NbLaps ; ++index ){
		for(long indexThread = 1 ; indexThread < NbThreads ; ++indexThread){
			pthread_create(&threads[indexThread], 0, PthreadWithId, (void*)indexThread);
		}
		PthreadWithId(0);
		for(long indexThread = 1 ; indexThread < NbThreads ; ++indexThread){
			pthread_join(threads[indexThread], 0);
		}
	}

	const double endTime = omp_get_wtime();

	std::cout << "Duration = " << (endTime - startTime) << std::endl;
}

/** Executes all functions */
int main(int argc, char *argv[]) {
	TestOpenmp();

	TestOpenmpWithId();

	TestOpenmpWithIdAndFunction();

	TestPthreadFunction();

	TestPthreadFunctionWithId();

	return 0;
}

Test Result/Output

--@ubuntu:~/--/--$ ./main
------- Test Openmp -------
Duration = 9.15405
------- Test Openmp With Id -------
Duration = 9.20959
------- Test Openmp With Id And Function-------
Duration = 9.1924
------- Test Pthread Function-------
Duration = 32.0271
------- Test Pthread Function With Id-------
Duration = 32.0481

Conclusion

Both API do not have the same objective. For software development PTHREAD may be better, for HPC and scientific applications OpenMP may be better (because it works faster and it is made to work on loop or fast creation/destruction).

Enjoyed reading this post?
Subscribe to the RSS feed and have all new posts delivered straight to you.

Comments are closed.

Celadon theme by the Themes Boutique