Built for an Operating Systems course, then cleaned up and documented as a standalone project.
Computing π with the Leibniz series in two versions — serial and POSIX threads — and measuring what actually happens to the runtime when you add threads.
The point of the project is the measurement, not the value of π.
5,000,000,000 iterations, Ubuntu on a UTM VM reporting 4 logical cores, gcc -Wall -Wextra -O2 -pthread:
| Version | Threads | Time (s) | Speedup |
|---|---|---|---|
| Serial | — | 3.0008 | 1.00× |
| Parallel | 1 | 2.9600 | 1.01× |
| Parallel | 2 | 1.5572 | 1.93× |
| Parallel | 4 | 0.8177 | 3.67× |
| Parallel | 8 | 0.8188 | 3.67× |
Two things worth reading off this table:
Scaling stops at the core count. 8 threads is not faster than 4 — it is a hair slower. The machine has 4 logical cores, so beyond that point the threads only take turns and add scheduling overhead. Near-linear speedup up to the physical limit, flat after it.
A 1-thread parallel run ≈ the serial run (1.01×). That is the sanity check that the speedup is real parallelism and not an accidental algorithmic change between the two implementations.
The computed values differ slightly between runs:
serial 3.141592653388201
parallel/2 3.141592653388271
parallel/8 3.141592653388992
This is expected and not a bug. Floating-point addition is not associative, and each thread sums its own slice in a different order before the partial sums are combined. The divergence grows with thread count because there are more partial sums to fold together.
The parallel version splits the iteration range into contiguous slices, one per thread. Each thread accumulates into its own partial_sum field inside its argument struct — there is no shared accumulator, so no mutex is needed on the hot loop and no false-sharing-heavy atomic is used. main joins all threads and folds the partial sums at the end.
pi = 4 * Σ (-1)^i / (2i + 1)
make
./pi_serial <iterations>
./pi_parallel <iterations> <thread_count>
# reproduce the table above
./pi_serial 5000000000
./pi_parallel 5000000000 4Both programs validate their arguments with strtoll and reject non-numeric or non-positive input.
pi_serial.c single-threaded reference implementation
pi_parallel.c pthread_create / pthread_join, per-thread partial sums
Makefile gcc -Wall -Wextra -O2 -pthread
results.txt raw measurements and test environment
docs/ design notes and full report
Author: Ali Erdem Gedik · github.com/aeg58