What is Parallel Programming?
In very simple terms, it is the use of multiple resources, in this case, processors, to solve a problem. This type of programming takes a problem, breaks it down into a series of smaller steps, delivers instructions, and processors execute the solutions at the same time.It is also a form of programming that offers the same results as concurrent programming but in less time and with more efficiency.
OpenCilk tutorial
Be aware that opencilk doesn't work for windows.
First we will download the docker image:
1)
wget https://github.com/OpenCilk/opencilk-project/releases/download/opencilk%2Fv1.0/docker-opencilk-v1.0.tar.gz
Then load the image :(which loads the image from a tar archive. It restores both images and tags.)
docker load < docker-opencilk-v1.0.tar.gz
2) Run a simple c program to confirm opencilk is installed:
#include <stdio.h>
#include <stdlib.h>
#define cilk_spawn _Cilk_spawn // the includes neccessary for openCilk
#define cilk_sync _Cilk_sync
#define cilk_for _Cilk_for
int main(){
printf("Hello world");
}
3)Quick overview:
Cilk extends C and C++ with three keywords: cilk_spawn, cilk_sync, and cilk_for
cilk_spawn and cilk_sync:
example 1:
int64_t fib(int64_t n) {
if (n < 2) return n;
int x, y;
x = cilk_spawn fib(n - 1);
y = fib(n - 2);
cilk_sync;
return x + y;
}
In the usage of cilk_spawn, parallel work is created when cilk_spawn precedes the invocation of a function, thereby causing the function to be spawned.The code immediately following the spawnβββis allowed to execute in parallel with the child, instead of waiting for the child to complete as is done in C/C++.
In the example fib function, the cilk_spawn spawns the recursive invocation of fib(n-1), allowing it to execute in parallel with its continuation, which calls fib(n-2).
The cilk_sync is a local "barrier," not a global one as. In fib, the cilk_sync prevents the execution of fib from continuing past the cilk_sync until the spawned invocation of fib(n-1) has returned.
cilk_for:
example 1:
void daxpy(int n, double a, double *x, double *y) {
cilk_for (int i = 0; i < n; ++i) {
y[i] = a * x[i] + y[i];
}
}
The cilk_for parallel-loop construct indicates that all iterations of the loop are allowed to execute in parallel. At runtime, these iterations can execute in any order, at the discretion of the runtime scheduler.
example 2:
void mm(const double *restrict A,
const double *restrict B,
double *restrict C,
int64_t n) {
cilk_for (int64_t i = 0; i < n; ++i) {
cilk_for (int64_t j = 0; j < n; ++j) {
for (int64_t k = 0; k < n; ++k) {
C[i*n + j] += A[i*n + k] * B[k*n + j];
}
}
}
}
```
To sum up, a good start would be using cilk_for to replace all the for in your synchronous code. Also using cilk_spawn and cilk_sync
for all your recursive calls.
Top comments (0)