Threads

Lab exercises for February 19th. Due Feb 20

The goals for this assignment are:

  • Understanding the pthread library

  • Working with ucontext

We will use the same repository as last week: Labs Repo. Click on the link and then accept and merge the pull request.

1. Matrix Add

In the files, matrix.c, implement a program that uses M threads to add two matrices, each stored in its own binary file, and saving the result into a third file.

$ ./matrix
usage: ./matrix   
$ ./matrix 1 A1 B2
Dimensions do not match!
$ ./matrix 1 A1 B1
Starting thread 139848748009024 (idx = 0)
duration: 0.00
$ ./readmat A1
0 -8 -6 8
0 4 -7 9
1 7 0 5
$ ./readmat B1
1 -6 -2 3
-6 -1 -9 8
-7 8 -5 -5
$ ./readmat A1_B1
1 -14 -8 11
-6 3 -16 17
-6 15 -5 0
$ ./matrix 1 A4 B4
Starting thread 139696590177856 (idx = 0)
duration: 0.36
$ ./matrix 4 A4 B4
Starting thread 140698237380160 (idx = 0)
Starting thread 140698228987456 (idx = 1)
Starting thread 140698220594752 (idx = 2)
Starting thread 140698212202048 (idx = 3)
duration: 0.21
$ ./matrix 8 A4 B4
Starting thread 140559920776768 (idx = 1)
Starting thread 140559912384064 (idx = 2)
Starting thread 140559903991360 (idx = 3)
Starting thread 140559929169472 (idx = 0)
Starting thread 140559895598656 (idx = 4)
Starting thread 140559887205952 (idx = 5)
Starting thread 140559878813248 (idx = 6)
Starting thread 140559870420544 (idx = 7)
duration: 0.23

Requirements/Hints:

  • Time how long it takes to compute the result when you have 8, 4, or a single thread.

  • Leverage spatial locality to ensure the best performance, e.g. split the work by rows in each matrix.

  • Use the program readmat.c to test that your program runs correctly

  • Name the output file based on the two inputs, e.g. A1_B1

  • Use gettimeofday(), defined in sys/time.h to get the duration of your program.

struct timeval ts, te;
gettimeofday(&ts, NULL);
...
gettimeofday(&te, NULL);
double time = te.tv_sec - ts.tv_sec + (te.tv_usec - ts.tv_usec)/1.e6;
printf("duration: %.4f\n", time);

The matrices you need to add are stored in a binary file that stores a sequence of integers.

  • The first integer is the number of rows

  • The second integer is the number of columns

  • The remaining integers are the values of the matrix, stored in row-major order.

To test with large matrices, download them from the website (but don’t check them in!)

$ wget https://cs.brynmawr.edu/~alinen/A4
$ wget https://cs.brynmawr.edu/~alinen/B4
Don’t try to check in large matrices to git! This will lead errors.

2. Funny functions

Implement the following programs based on ucontext.

2.1. Return

In the file, hello_context.c, modify the program so it returns to main after executing f(). Use the code from lecture as a guide.

$ ./hello_context
Hello World
End of main

2.2. Alternate

In the file, alternate.c, write a program that alternates between two functions indefinitely.

$ ./alternate
odd:1
even:0
odd:3
even:2
odd:5
^C

Hints:

  • Alternate between main and another function.

  • Make your two ucontext variables global so that main can swap to the function, and then the function can swap back to main

  • The example above uses nanosleep to pause inside the loop

  • The example above sets printf to flush immediately (e.g. show output text immediately) to make the output cleaner. This is done with the command setvbuf(stdout, 0, _IOLBF, 0);.

Above, main loops over odd numbers and the function loops over even numbers (see below). Each iteration, they swapcontext.

void nextEven()
{
  struct timespec delay = { 1, 0 };
  for (unsigned int i = 0;; i += 2) {
    printf("even:%d\n", i);
    nanosleep(&delay, 0);
  }
}

int main(int argc, char * argv[])
{
  // flush each printf as it happens
  setvbuf(stdout, 0, _IOLBF, 0);

  // etc
}

2.3. Alarm

In the file, alarm.c, write a program that alternates between two functions at a set interval. Use a kernel timer like in the sig_alarm demo from class.

$ **./alarm**
even:0
even:2
odd:1
odd:3
even:4
even:6
odd:5
odd:7

Hints:

  • Above, we defined two functions in addition to main, nextEven() and nextOdd().

  • The signal handler switches between nextEven and nextOdd using a global variable that specifies which should run

  • After setting up the contexts and signals, main calls pause in an infinite loop.

  • The timer triggers every 2 seconds and each functions sleep 1 second between printing. Thus, we see two prints before switching to the next function.