Commit e20c8569 authored by Luca Tornatore's avatar Luca Tornatore
Browse files

first commit of the readme

parent bc28979a
Loading
Loading
Loading
Loading
+173 −0
Changes for OpenMP/examples/tasks/basic_task_examples/README.md: 173 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Basic examples on OpenMP tasks



[TOC]

## List of the examples

- `00_simple` : let's start with some fundamental properties and dynamics of task creation
- `02_tasks` : we accumulate partial results from tasks
- `03_variable_workload`: we create a mutable amount of tasks, basically implementing a `taskloop` directive by hands
- `04_tasks_reduction`: work has to be done for data that "arrives" in irregular chunks at irregular timing
- `04_unpredictable_pattern`: as before, with random workloads
- `05_taskgrou_reduction`: an example of a task-wide reduction

## 00_simple

The first example, `00_simple.c` offers to opportunity of exploring many key concepts about tasks.

#### Step 0, `00_simple.c`

`````c
#pragma omp parallel
 {
   #pragma omp single
	{
		printf( " »Yuk yuk, here is thread %d from "
				"within the single region\n", omp_get_thread_num() );
       #pragma omp task
		{
			printf( "\tHi, here is thread %d "
					"running task A\n", omp_get_thread_num() );
		}
	   #pragma omp task
		{
			printf( "\tHi, here is thread %d "
			"running task B\n", omp_get_thread_num() );
		}
	}

	printf(" :Hi, here is thread %d after the end "
		   "of the single region, I was stuck waiting "
		   "all the others\n", omp_get_thread_num() );
}

`````

Two tasks, "A" and "B" are created inside the `single` region. They consist in writing a greetings message, with the additional information of the id of the executing task.
Note that the final greetings "`` :Hi, here is thread … I was stuck..``" is always written *after* all the greetings by the tasks execution.
This is due to the TSP (Task Scheduling Point) of the implicit `barrier` at the end of the `single` region.

Also note that the task execution requires the calling of the function `omp_get_thread_num()`; this is correct, because at the creation time we do not know who the executing task will be.

The following version also works correctly, and is even more elegant:

```c
 int myid;
#pragma omp threadprivate(myid)

#pragma omp parallel
 {
     myid = omp_get_thread_num();
   #pragma omp single
	{
	   #pragma omp task
		{
			printf( "\tHi, here is thread %d "
					"running task A\n", myid );
		}
    }
 }

```

`myid` is a `threadprivate` variable; it means that every thread has 4 bytes for all the duration of the code, that are initialized in the parallel region to its own id. Hence, at the moment of task execution, the value of the myid will always be correct.
At odds, writing as follows would be wrong, because the myid would always be that of the generating thread;

```c
#pragma omp parallel
 {
    int myid = omp_get_thread_num();
   #pragma omp single
	{
	   #pragma omp task
		{
			printf( "\tHi, here is thread %d "
					"running task A\n", myid );
		}
    }
 }
```

#### Step 1, `00_simple_nowait.c`

Adding a `nowait` clause to the `single` directive

```c
#pragma omp parallel
 {
   #pragma omp single nowait
	{
	   #pragma omp task
		{
            myid = omp_get_thread_num() + 1;
            for ( int i = 0; i < myid; i++)
				printf( "\tHi, here is thread %d "
						"running task A_%03d\n",
                       omp_get_thread_num(), i );
		}
    }
     printf(":Hi, I'm not waiting at the end of the single region")
 }
```

makes all the threads that do no execute the `single` region to continue the execution of the code after the `single` region.
The only thing ensured by the standard, is that they *at least* we'll be waiting the the next TSP which, in this case, is the closing of the parallel region.
So, we *can not assume* that any generated task is executed at any precise time before the end of the parallel region.

Note that the loop counter `i` is private for the generating thread, and hence it will be `firstprivate` for every task, that then gets the correct value of `i` embedded in its data environment.

#### Step 2, `00_simple_taskwait.c`

```c
#pragma omp parallel
 {
   #pragma omp single nowait
	{
	   #pragma omp task
		{
            myid = omp_get_num_thread();
            printf("thread %d is the generator thread\n")
            for ( int i = 0; i < myid+1; i++)
				printf( "\tHi, here is thread %d "
						"running task A_%03d\n",
                       omp_get_thread_num(), i );
           #pragma omp taskwait                     // taskwait A
            printf("thread %d is now beyond the taskwait A line\n")
		}
    }
    #pragma omp taskwait                            // taskwait B
     
     printf(":Hi, there I'm th %d I'm beyond taskwait B",
           omp_get_thread_num());
 }
```

The `taskwait` within the single region (taskwait A) will have the expected effect: delaying the generating thread until all its child tasks created in that region (not the grandsons) have been executed.
The taskwait B, at odds, has no effect: all the threads that were not in the `single` region are not affected *because they have no child tasks* in the current region; in turn, the thread that generated the tasks is there exactly because all its child tasks have already been executed (they were already beyond the taskwait A)



## 02_tasks

TBD



## 03_ variable_workload

TBD



## 04_task_reduction

TBD



## 05_taskgroup_reduction

TBD