diff --git a/2-resources.ipynb b/2-resources.ipynb index a5b5cf0..ab698c3 100644 --- a/2-resources.ipynb +++ b/2-resources.ipynb @@ -47,17 +47,19 @@ "For example : [`scripts/mpi-hello.sh`](https://github.com/calculquebec/cip201-compute-systems/blob/main/scripts/mpi-hello.sh)\n", "\n", "```Bash\n", - "cat scripts/mpi-hello.sh\n", + "cat scripts/mpi-pi/pi-job.sh\n", "```\n", "```\n", "#!/bin/bash\n", - "#SBATCH --ntasks=10\n", - "#SBATCH --mem-per-cpu=1000M\n", - "#SBATCH --time=0-00:10\n", + "\n", + "#SBATCH --job-name=pi\n", + "#SBATCH --ntasks=4\n", + "#SBATCH --mem-per-cpu=1G\n", + "#SBATCH --time=00:05:00\n", "\n", "module load StdEnv/2023 gcc/12.3 openmpi/4.1.5\n", "\n", - "mpirun printenv HOSTNAME OMPI_COMM_WORLD_RANK OMPI_COMM_WORLD_SIZE\n", + "srun ./pi 10000000000\n", "```\n", "\n", "Our documentation about job scripts starts at this page:\n", diff --git a/scripts/array-distrib/README.txt b/scripts/array-distrib/README.txt new file mode 100644 index 0000000..394bbd8 --- /dev/null +++ b/scripts/array-distrib/README.txt @@ -0,0 +1,11 @@ +Array job generating random normal distributions +------------------------------------------------ + +Generate normal distributions (1000 points each) with random mean (0 to 1) and +std (0.5 to 1.5), save them to a CSV file, and print statistics. The Python code +is in `distrib.py`. An array job is used to generate 4 distributions saved to +the `results` directory. + +Submit the array job script to the scheduler: + +$ sbatch distrib-job.sh diff --git a/scripts/array-distrib/distrib-job.sh b/scripts/array-distrib/distrib-job.sh new file mode 100644 index 0000000..dda6d2f --- /dev/null +++ b/scripts/array-distrib/distrib-job.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --array=1-4 +#SBATCH --ntasks=1 +#SBATCH --mem=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 python/3.11.5 scipy-stack/2024b + +mkdir -p results +output_filename="results/distrib-${SLURM_ARRAY_TASK_ID}.csv" + +srun python3 ./distrib.py "${output_filename}" diff --git a/scripts/array-distrib/distrib.py b/scripts/array-distrib/distrib.py new file mode 100644 index 0000000..a68e2ee --- /dev/null +++ b/scripts/array-distrib/distrib.py @@ -0,0 +1,20 @@ +from sys import argv, exit + +import numpy as np +import pandas as pd + +if len(argv) != 2: + print('usage: python3 distrib.py ') + exit(1) + +output_filename = argv[1] + +mean = np.random.rand() +std = np.random.rand() + 0.5 +dist = np.random.normal(mean, std, size = 1000) +df = pd.DataFrame(dist) + +with open(output_filename, 'w') as f: + f.write(df.to_csv()) +print(f'Random normal distribution writen to {output_filename}.') +print(df.describe()) diff --git a/scripts/job-script-templates/array-job.sh b/scripts/job-script-templates/array-job.sh new file mode 100644 index 0000000..9f93013 --- /dev/null +++ b/scripts/job-script-templates/array-job.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --array=0-9 +#SBATCH --ntasks=1 +#SBATCH --mem=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 + +srun my-serial-prog ${SLURM_ARRAY_TASK_ID} diff --git a/scripts/job-script-templates/hybrid-by-node-job.sh b/scripts/job-script-templates/hybrid-by-node-job.sh new file mode 100644 index 0000000..29b1c50 --- /dev/null +++ b/scripts/job-script-templates/hybrid-by-node-job.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=2 +#SBATCH --cpus-per-task=2 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +export OMP_NUM_THREADS="${SLURM_CPUS_PER_TASK:-1}" + +module load StdEnv/2023 gcc/12.3 openmpi/4.1.5 + +srun my-hybrid-prog arg1 arg2 diff --git a/scripts/job-script-templates/hybrid-job.sh b/scripts/job-script-templates/hybrid-job.sh new file mode 100644 index 0000000..04d70aa --- /dev/null +++ b/scripts/job-script-templates/hybrid-job.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --ntasks=2 +#SBATCH --cpus-per-task=2 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +export OMP_NUM_THREADS="${SLURM_CPUS_PER_TASK:-1}" + +module load StdEnv/2023 gcc/12.3 openmpi/4.1.5 + +srun my-hybrid-prog arg1 arg2 diff --git a/scripts/job-script-templates/mpi-by-node-job.sh b/scripts/job-script-templates/mpi-by-node-job.sh new file mode 100644 index 0000000..c28f287 --- /dev/null +++ b/scripts/job-script-templates/mpi-by-node-job.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=4 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 openmpi/4.1.5 + +srun my-mpi-prog arg1 arg2 diff --git a/scripts/job-script-templates/mpi-job.sh b/scripts/job-script-templates/mpi-job.sh new file mode 100644 index 0000000..b32f878 --- /dev/null +++ b/scripts/job-script-templates/mpi-job.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --ntasks=4 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 openmpi/4.1.5 + +srun my-mpi-prog arg1 arg2 diff --git a/scripts/job-script-templates/openmp-job.sh b/scripts/job-script-templates/openmp-job.sh new file mode 100644 index 0000000..2750ec3 --- /dev/null +++ b/scripts/job-script-templates/openmp-job.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=4 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 + +srun my-openmp-prog arg1 arg2 diff --git a/scripts/job-script-templates/serial-job.sh b/scripts/job-script-templates/serial-job.sh new file mode 100644 index 0000000..68101f1 --- /dev/null +++ b/scripts/job-script-templates/serial-job.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +#SBATCH --job-name=myjob +#SBATCH --ntasks=1 +#SBATCH --mem=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 + +srun my-serial-prog arg1 arg2 diff --git a/scripts/mpi-pi/README.txt b/scripts/mpi-pi/README.txt new file mode 100644 index 0000000..0742488 --- /dev/null +++ b/scripts/mpi-pi/README.txt @@ -0,0 +1,15 @@ +MPI pi estimate using Monte Carlo +--------------------------------- + +Estimate the pi number, the ratio of a circle’s circumference to its diameter, +using a Monte Carlo approach. This is an MPI (Message Passing Interface) +parallel program. The C code is in `pi.c`. + +1. Compile the program: + +$ module load StdEnv/2023 gcc/12.3 openmpi/4.1.5 +$ mpicc -o pi pi.c -lm + +2. Submit the job script to the scheduler: + +$ sbatch pi-job.sh diff --git a/scripts/mpi-pi/pi-job.sh b/scripts/mpi-pi/pi-job.sh new file mode 100644 index 0000000..307e968 --- /dev/null +++ b/scripts/mpi-pi/pi-job.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +#SBATCH --job-name=pi +#SBATCH --ntasks=4 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 openmpi/4.1.5 + +srun ./pi 10000000000 diff --git a/scripts/mpi-pi/pi.c b/scripts/mpi-pi/pi.c new file mode 100644 index 0000000..cdceaf6 --- /dev/null +++ b/scripts/mpi-pi/pi.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +double monte_carlo_pi(unsigned long long n, MPI_Comm comm) +{ + int rank, nranks; + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &nranks); + srand(time(NULL) + rank); + double x, y; + unsigned long long local_p = 0; + for (unsigned long long i = rank; i < n; i += nranks) + { + x = (double) rand() / RAND_MAX; + y = (double) rand() / RAND_MAX; + if (sqrt(x*x + y*y) <= 1.0) + { + local_p++; + } + } + unsigned long long p; + MPI_Allreduce(&local_p, &p, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, comm); + return 4*((double)p)/n; +} + +int main(int argc, char* argv[]) +{ + MPI_Init(&argc, &argv); + + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + if (argc == 2) + { + char* endptr; + unsigned long long n = strtoll(argv[1], &endptr, 10); + + double pi = monte_carlo_pi(n, MPI_COMM_WORLD); + if (rank == 0) + { + printf("After %llu points, pi estimate is %f.\n", n, pi); + } + } + else + { + if (rank == 0) + { + printf("usage: pi \n"); + } + } + + MPI_Finalize(); + return 0; +} diff --git a/scripts/openmp-primes/README.txt b/scripts/openmp-primes/README.txt new file mode 100644 index 0000000..5fdd133 --- /dev/null +++ b/scripts/openmp-primes/README.txt @@ -0,0 +1,14 @@ +OpenMP count of prime numbers +----------------------------- + +Count the number of prime numbers from 1 to N. This is a parallel OpenMP +program. The C code is in `primes.c`. + +1. Compile the program: + +$ module load StdEnv/2023 gcc/12.3 +$ mpicc -o primes primes.c -fopenmp + +2. Submit the job script to the scheduler: + +$ sbatch primes-job.sh diff --git a/scripts/openmp-primes/primes-job.sh b/scripts/openmp-primes/primes-job.sh new file mode 100644 index 0000000..e91aa60 --- /dev/null +++ b/scripts/openmp-primes/primes-job.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +#SBATCH --job-name=primes +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=4 +#SBATCH --mem-per-cpu=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 + +srun ./primes 400000 diff --git a/scripts/openmp-primes/primes.c b/scripts/openmp-primes/primes.c new file mode 100644 index 0000000..567ef42 --- /dev/null +++ b/scripts/openmp-primes/primes.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +bool* find_primes(unsigned long n) +{ + bool* is_prime = malloc(n * sizeof(bool)); + #pragma omp parallel + { + int thread = omp_get_thread_num(); + int nthreads = omp_get_num_threads(); + for (unsigned long i = thread; i < n; i+= nthreads) + { + is_prime[i] = true; + for (unsigned long j = 2; j < i; j++) + { + if (i % j == 0) + { + is_prime[i] = false; + } + } + } + } + is_prime[0] = false; + return is_prime; +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + printf("usage: primes \n"); + return(1); + } + unsigned long n = atol(argv[1]); + + bool* is_prime = find_primes(n); + int nprimes = 0; + for (int i = 0; i < n; i++) + { + if (is_prime[i]) + { + nprimes++; + } + } + printf("There are %d primes up to %lu.\n", nprimes, n); + + return 0; +} diff --git a/scripts/serial-fibonacci/README.txt b/scripts/serial-fibonacci/README.txt new file mode 100644 index 0000000..6031458 --- /dev/null +++ b/scripts/serial-fibonacci/README.txt @@ -0,0 +1,15 @@ +Serial Fibonacci using recursion +-------------------------------- + +Compute the Nth number in the Fibonacci sequence using recursion. This is a +serial program: computing Fibonacci numbers cannot be done in parallel. The C +code is in `fibonacci.c`. + +1. Compile the program: + +$ module load StdEnv/2023 gcc/12.3 +$ gcc -o fibonacci fibonacci.c + +2. Submit the job script to the scheduler: + +$ sbatch fibonacci-job.sh diff --git a/scripts/serial-fibonacci/fibonacci-job.sh b/scripts/serial-fibonacci/fibonacci-job.sh new file mode 100644 index 0000000..dbd8843 --- /dev/null +++ b/scripts/serial-fibonacci/fibonacci-job.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +#SBATCH --job-name=fibo +#SBATCH --ntasks=1 +#SBATCH --mem=1G +#SBATCH --time=00:05:00 + +module load StdEnv/2023 gcc/12.3 + +srun ./fibo 50 diff --git a/scripts/serial-fibonacci/fibonacci.c b/scripts/serial-fibonacci/fibonacci.c new file mode 100644 index 0000000..7319d6a --- /dev/null +++ b/scripts/serial-fibonacci/fibonacci.c @@ -0,0 +1,32 @@ +#include +#include + +unsigned long fibonacci(unsigned int nth) +{ + if (nth == 0) + { + return 0; + } + else if (nth == 1) + { + return 1; + } + else + { + return fibonacci(nth - 1) + fibonacci(nth - 2); + } +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + printf("usage: fibo \n"); + return(1); + } + unsigned int nth = atoi(argv[1]); + + printf("The %uth Fibonacci number is %lu.\n", nth, fibonacci(nth)); + + return 0; +}