// adapted from ostep 26-threads-intro/t1.c // modified to compare serial vs. parallel // #include #include #include #include #define Pthread_create(thread,attr,start_routine,arg) \ assert(pthread_create(thread,attr,start_routine,arg) == 0) #define Pthread_join(thread,value_ptr) \ assert(pthread_join(thread,value_ptr) == 0) #define Pthread_mutex_lock(m) \ assert(pthread_mutex_lock(m) == 0) #define Pthread_mutex_unlock(m) \ assert(pthread_mutex_unlock(m) == 0) int max; volatile int counter = 0; // shared global variable void *mythread(void *arg) { char *letter = arg; int i; // stack (private per thread) printf("%s: begin [addr of i: %p]\n", letter, &i); for (i = 0; i < max; i++) { counter = counter + 1; // shared: only one } printf("%s: done\n", letter); return NULL; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: main-first \n"); exit(1); } max = atoi(argv[1]); printf("main: begin [counter = %d] [%p]\n", counter, &counter); #ifdef SERIAL // no threads, just run sequentially mythread("A"); mythread("B"); #else // original code pthread_t p1, p2; Pthread_create(&p1, NULL, mythread, "A"); Pthread_create(&p2, NULL, mythread, "B"); // join waits for the threads to finish Pthread_join(p1, NULL); Pthread_join(p2, NULL); #endif printf("main: done\n [counter: %d]\n [should: %d]\n", counter, max*2); return 0; } /* testing: $ gcc -Wall -o parallel c26t1.c -pthread $ gcc -Wall -DSERIAL -o serial c26t1.c -pthread On multi-core system: parallel takes longer due to hardware maintaining cache consistency between cores $ time ./serial 1000000000 main: begin [counter = 0] [0x555b3049602c] A: begin [addr of i: 0x7fff1bdbbb3c] A: done B: begin [addr of i: 0x7fff1bdbbb3c] B: done main: done [counter: 2000000000] [should: 2000000000] real 0m2.951s user 0m2.951s sys 0m0.001s $ time ./parallel 1000000000 main: begin [counter = 0] [0x55aa93ac702c] A: begin [addr of i: 0x7f3c001d5edc] B: begin [addr of i: 0x7f3bff9d4edc] A: done B: done main: done [counter: 1023127523] [should: 2000000000] real 0m6.162s user 0m12.249s sys 0m0.001s $ ---------------------------------------------------------------------- On single-core system (fog): parallel and serial take about the same time $ time ./serial 1000000000 main: begin [counter = 0] [0x601064] A: begin [addr of i: 0x7ffd5e732bb4] A: done B: begin [addr of i: 0x7ffd5e732bb4] B: done main: done [counter: 2000000000] [should: 2000000000] real 0m3.397s user 0m3.390s sys 0m0.001s $ time ./parallel 1000000000 main: begin [counter = 0] [0x60107c] A: begin [addr of i: 0x7fb5e5db8f04] B: begin [addr of i: 0x7fb5e55b7f04] A: done B: done main: done [counter: 1930978874] [should: 2000000000] real 0m3.386s user 0m3.377s sys 0m0.001s $ */