Commit 3f2e25b4 authored by Taddeüs Kroes's avatar Taddeüs Kroes

ModSim ass4 taddeus: Worked on report.

parent 6eeac410
#!/bin/bash
if [ $# -lt 3 ]; then
echo "Usage: bash $0 MAX STEP STRING_STEPS"
exit 1
fi
dx=`echo "1/$3" | bc -l`
args="sinus 1000 1 $dx 1 2"
# Execute sequential program
#/usr/bin/time -f "%e" --quiet ./seq $args
# Execute parallel program for different numbers of nodes
for (( i=2; i <= $1; i += $2 )); do
echo `/usr/bin/time -f "$i %e" --quiet ./par.sh $i $args`
done
#!/bin/bash #!/bin/bash
if [ $# -lt 7 ]; then if [ $# -lt 7 ]; then
echo "Usage: $0 NUM_PROCESSES INIT_METHOD TIME_STEPS LENGTH DX TAU N|XP" echo "Usage: bash $0 NUM_PROCESSES INIT_METHOD TIME_STEPS LENGTH DX TAU N|XP"
exit 1 exit 1
fi fi
./seq ${@:2:$#} > out_seq.txt ./seq ${@:2:$#} > out_seq.txt
......
...@@ -3,13 +3,14 @@ ...@@ -3,13 +3,14 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <mpi.h> #include <mpi.h>
#include <assert.h>
#define VERBOSE //#define VERBOSE
double **y = NULL; double **y = NULL;
int tasks, rank, steps, start; int tasks, rank, steps, start;
#ifdef VERBOSE
FILE *file; FILE *file;
#endif
/* /*
* Connect the string at both ends by sending the calculated border values and * Connect the string at both ends by sending the calculated border values and
...@@ -32,6 +33,10 @@ void interact(int state) { ...@@ -32,6 +33,10 @@ void interact(int state) {
} }
} }
/*
* Initialize the left- and right values of the string section. The outer
* values are sent by other nodes.
*/
void init_borders() { void init_borders() {
// The string is attached at both ends // The string is attached at both ends
if( !rank ) if( !rank )
...@@ -43,7 +48,7 @@ void init_borders() { ...@@ -43,7 +48,7 @@ void init_borders() {
} }
/* /*
* Fill the assigned string section with a sinus shape * Fill the assigned string section with a sinus shape.
*/ */
void init_sinus(double l, double dx, double n) { void init_sinus(double l, double dx, double n) {
for( int i = 0; i < steps; i++ ) for( int i = 0; i < steps; i++ )
...@@ -53,7 +58,7 @@ void init_sinus(double l, double dx, double n) { ...@@ -53,7 +58,7 @@ void init_sinus(double l, double dx, double n) {
} }
/* /*
* Fill the assigned string section with a plucked shape * Fill the assigned string section with a plucked shape.
*/ */
void init_plucked(double l, double dx, double xp) { void init_plucked(double l, double dx, double xp) {
double x; double x;
...@@ -69,6 +74,10 @@ void init_plucked(double l, double dx, double xp) { ...@@ -69,6 +74,10 @@ void init_plucked(double l, double dx, double xp) {
#ifdef VERBOSE #ifdef VERBOSE
#define PRINT_FORMAT "%e" #define PRINT_FORMAT "%e"
/*
* Print all calculated values in the string section of one of the three
* currently known time states.
*/
void print(int index) { void print(int index) {
for( int i = 1; i <= steps; i++ ) { for( int i = 1; i <= steps; i++ ) {
if( i > 1 ) if( i > 1 )
...@@ -82,7 +91,8 @@ void print(int index) { ...@@ -82,7 +91,8 @@ void print(int index) {
#endif #endif
/* /*
* * Calculate and optionally print the string section states for the specified
* number of time steps.
*/ */
void calculate_steps(int time, double dx, double tau) { void calculate_steps(int time, double dx, double tau) {
double x; double x;
...@@ -134,11 +144,13 @@ void calculate_steps(int time, double dx, double tau) { ...@@ -134,11 +144,13 @@ void calculate_steps(int time, double dx, double tau) {
} }
} }
/*
* Main method, initiates the program.
*/
int main(int argc, char **argv) { int main(int argc, char **argv) {
int error, time, i, total_steps; int error, time, i, total_steps;
void (*init_method)(double, double, double); void (*init_method)(double, double, double);
double l, tau, constant, dx; double l, tau, constant, dx;
char filename[9];
// Parse arguments // Parse arguments
if( argc < 7 ) { if( argc < 7 ) {
...@@ -172,9 +184,12 @@ int main(int argc, char **argv) { ...@@ -172,9 +184,12 @@ int main(int argc, char **argv) {
MPI_Comm_size(MPI_COMM_WORLD, &tasks); MPI_Comm_size(MPI_COMM_WORLD, &tasks);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_rank(MPI_COMM_WORLD, &rank);
#ifdef VERBOSE
// Open file for output // Open file for output
char filename[9];
sprintf(filename, "out%d.txt", rank); sprintf(filename, "out%d.txt", rank);
file = fopen(filename, "w"); file = fopen(filename, "w");
#endif
// Calculate the indices of the string to calculate // Calculate the indices of the string to calculate
total_steps = (int)ceil(l / dx) + 1; total_steps = (int)ceil(l / dx) + 1;
...@@ -206,8 +221,10 @@ int main(int argc, char **argv) { ...@@ -206,8 +221,10 @@ int main(int argc, char **argv) {
free(y); free(y);
fclose(file);
MPI_Finalize(); MPI_Finalize();
#ifdef VERBOSE
fclose(file);
#endif
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#!/bin/bash #!/bin/bash
if [ $# -lt 7 ]; then if [ $# -lt 7 ]; then
echo "Usage: $0 NUM_PROCESSES INIT_METHOD TIME_STEPS LENGTH DX TAU N|XP" echo "Usage: bash $0 NUM_PROCESSES INIT_METHOD TIME_STEPS LENGTH DX TAU N|XP"
exit 1 exit 1
fi fi
mpirun -np ${1-4} ./par ${@:2:$#} mpirun -np ${1-4} ./par ${@:2:$#}
......
#!/usr/bin/env python
from sys import stdin, argv
from pylab import plot, show, savefig
# Collect data
x = []
y = []
print stdin.readlines()
for line in stdin.readlines():
n, t = line.split(' ')
x.append(int(n))
y.append(float(t))
# Plot data
print x, y
plot(x, y, 'o-')
if len(argv) == 2:
savefig(argv[1])
show()
...@@ -121,17 +121,16 @@ $y(x_i, t_{j + 1})$ kan worden berekend. In berekeningsstappen is dit als volgt ...@@ -121,17 +121,16 @@ $y(x_i, t_{j + 1})$ kan worden berekend. In berekeningsstappen is dit als volgt
uit te beelden: uit te beelden:
\begin{table}[H] \begin{table}[H]
\begin{tabular}{rc} \begin{tabular}{rc}
$\stackrel{\uparrow}{t}$ & & $x \rightarrow$ \\
$ & \\
\left. \begin{array}{ccccc} $\underset{\downarrow}{t}$ &
$\left. \begin{array}{ccccc}
1 & 2 & 3 & 4 & \ldots \\ 1 & 2 & 3 & 4 & \ldots \\
4 & 5 & 6 & \ldots & \ldots \\ 4 & 5 & 6 & \ldots & \ldots \\
6 & 7 & \ddots & \ldots & \ldots \\ 6 & 7 & \ddots & \ldots & \ldots \\
8 & \vdots & \vdots & \ddots & \ldots \\ 8 & \vdots & \vdots & \ddots & \ldots \\
\vdots & \vdots & \vdots & \vdots & \ddots \\ \vdots & \vdots & \vdots & \vdots & \ddots \\
\end{array} \right. \end{array} \right.$ \\
$ \\
& $x \rightarrow$ \\
\end{tabular} \end{tabular}
\end{table} \end{table}
...@@ -151,19 +150,151 @@ van het snaargedeelte). ...@@ -151,19 +150,151 @@ van het snaargedeelte).
\subsection{Sequentiëel} \subsection{Sequentiëel}
Het sequentiële programma heeft een simpele opzet: de \texttt{main}-methode
interpreteert de parameters en alloceert het benodigde geheugen. vervolgend
wordt een initialisatiemethode aangeroepen, en daarna de berekeningsmethode.
tot slot wordt het gealloceerde geheugen weer vrijgegeven.
de initialisatie van de snaar wordt gedaan in een aparte functie die eigen is
aan de initialisatiemethode (in dit geval alleen ``sinus'' en ``plucked'')
zodat eventueel op eenvoudige wijze een nieuwe initialisatiemethode kan worden
toegevoegd.
De eigenschappen van de snaar worden aan het programma meegegeven
als parameters in de volgende volgorde:
\begin{enumerate}
\item \texttt{INIT\_METHOD} \\
De te gebruiken initialisatiemethode: ``sinus'' of ``plucked''.
\item \texttt{CALCULATION\_STEPS} \\
Het aantal te berekenen tijdstappen.
\item \texttt{LENGTH} \\
De lengte van de snaar in meters.
\item \texttt{DX} \\
De stapgrootte voor de afstandsdimensie in meters.
\item \texttt{TAU} \\
De waarde van $\tau$.
\item \texttt{N|XP} \\
Een constante die eigen is aan de initialisatiemethode: $n$ voor
``sinus'', $x_p$ voor ``plucked''.
\end{enumerate}
\subsubsection*{Cyclische buffer}
Om te voorkomen dat bij een groot aantal tijdstappen een enorme hoeveelheid
geheugen wordt gealloceerd, worden op elk moment alleen de uitwijkingen in
laatste twee tijdstappen opgeslagen. Om dit te realiseren implementeert het
programma een cyclische buffer: de indices van de gealloceerde lijsten
verschuiven elke tijdstap zodat telkens de oudste waardes worden overschreven
met de nieuwe. Dit verschuiven gebeurt op regel 101-103:
\begin{verbatim}
prev = current;
current = next;
next = (next + 1) % 3;
\end{verbatim}
Door het gebruik van deze buffer is het geheugengebruik van het programma
proportioneel aan het aantal afstandsstappen (ofwel
$\mathcal{O}(\frac{\texttt{LENGTH}}{\texttt{DX}})$), en onafhankelijk van het
aantal tijdstappen.
\subsubsection*{Verbositeit}
De uitvoer van het programme is alleen interessant wanneer deze ook
daadwerkelijk wordt gebruikt, bijvoorbeeld in een grafiek. Echter, de
mogelijkheid moet bestaan om de uitvoer uit de programmacode te filteren, zodat
alleen de tijd het berekenen kan worden gemeten. Hiervoor heeft het prrogramma
een definitie:
\begin{verbatim}
#define VERBOSE
\end{verbatim}
Wanneer deze definitie in commentaar wordt geplaatst zullen alle print
statements worden genegeerd tijdens de compilatie (ook de functie
\texttt{print} zelf). Dit gebeurt op deze manier, alvorens te compileren,
zodat bij de uitvoer van een benchmark geen enkele statement verloren gaat aan
uitvoer van het programma.
\subsection{Parallel} \subsection{Parallel}
Het parallelle programma is een uitbreiding van het sequentiële programma, en
onderscheidt zich slechts door de volgende verschillen:
\begin{itemize}
\item De \texttt{main}-methode alloceert nu alleen het geheugen dat nodig
is voor het gedeelte van de snaar dat bij het uitgevoerde proces hoort.
\item De functie \texttt{interact} is toegevoegd. Deze functie wordt
uitgevoerd na het berekenen van alle uitwijkingen op het snaargedeelte
in een bepaalde tijdstap, en dient voor de communicatie van randwaardes
naar en vanuit de omliggende processen.
\item De uitvoer van het programma wordt niet meer geschreven naar
\texttt{stdout}, maar naar elk proces schrijft naar een eigen bestand
(uiteraard alleen wanneer \texttt{VERBOSE} is gedefiniëeerd). Deze
bestanden kunnen later weer worden gecombineerd tot een enkele reeks
waarden m.b.v. \emph{compine.py}.
\end{itemize}
\subsection{Scripts}
Voor het uitvoeren van het parallelle programma zijn verschillende
shell- en python-scripts aanwezig:
\begin{itemize}
\item \emph{par.sh} \\
Voert het parallelle programma uit door \texttt{mpirun} aan te roepen.
De eerste parameter is het aantal te starten processen, de rest van de
parameters wordt meegegeven aan het parallelle programma.
\item \emph{diff.sh} \\
Voert zowel het sequentiële als het parallelle proces uit en vergelijk
de uitvoer van beide programma's. Het verschil wordt geprint m.b.v. het
commando \texttt{diff}. Als alles goed gaat is de uitvoer van dit
script dus leeg. De parameters van dt programma zijn gelijk aan de
parameters voor \emph{par.sh}.
\item \emph{benchmark.sh} \\
Voert een aantal tijdmetingen uit voor een verschillend aantal nodes
meegegeven voor parallelle programma. De eerste twee parameters bepalen
de aantallen nodes voor de benchmark. De laatste parameter geeft het
aantal gewenste afstansstappen (gelijk aan $\frac{1}{\texttt{DX}}$).
\item \emph{combine.py} \\
Combineert de uitvoer van de verschillende parallelle processen tot een
enkele reeks (zoals de uitvoer van het sequentiële programma zou zijn).
De enige parameter voor dit script is het aantal processen dat is
uitgevoerd (dat gelijk is aan het aantal bestanden met uitvoer).
\item \emph{draw.py} \\
Toont een animatie van een reeks gegenereerde data. De parameter van
dit programma is de printresolutie. Als hier bijvoorbeeld 10 voor wordt
opgegegeven, wordt van de gegeven invoer (gelezen van \texttt{stdin})
elke 10 regels een regel geprint. Als tweede parameter kan de tijd
tussen twee frames worden opgegeven in seconden (standaard $0.10s$).
\item \emph{plot.py} \\
Zelfde doeleinde als \emph{draw.py}, maar produceert een statische
serie grafieken (met name voor gebruik in het verslag).
\end{itemize}
\subsection{Gebruik}
\subsection{Shell scripts} Hier volgen ter verduidelijking enkele voorbeelden van het (bedoelde) gebruik
van bovenstaande scripts:
\begin{verbatim}
# Sequentiëel programma
$ ./seq sinus 1000 1 .01 1 2
# Parallel programma (4 processen)
$ ./par.sh 4 sinus 1000 1 .01 1 2
\subsection{Gebruik} # Animatie sequentiële programma
$ ./seq sinus 1000 1 .01 1 2 | ./draw.py
# Animatie parallelle programma
$ ./par.sh 4 sinus 1000 1 .01 1 2; ./combine.py 4 | ./draw.py
# Verschillen in programma's (lege uitvoer)
$ ./diff.sh 4 sinus 1000 1 .01 1 2
$ ...
# Benchmark voor 2, 4, 6 en 8 nodes met 1000 afstandsstappen (DX = .01)
$ ./benchmark.sh 8 2 1000
\end{verbatim}
Indien een verkeerd aantal parameters wordt meegegeven zal elk programma en
script ook afzonderlijk een melding printen over het correcte gebruik \\
(``Usage: ...'').
\section{Resultaten} \section{Resultaten}
...@@ -178,8 +309,6 @@ van het snaargedeelte). ...@@ -178,8 +309,6 @@ van het snaargedeelte).
\appendix
%\begin{figure}[H] %\begin{figure}[H]
% \label{fig:sinus} % \label{fig:sinus}
% \includegraphics[width=15cm]{sinus.pdf} % \includegraphics[width=15cm]{sinus.pdf}
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#define VERBOSE //#define VERBOSE
double **y = NULL, dx; double **y = NULL, dx;
int steps, time; int steps, time;
/* /*
* * Fill the string with a sinus shape.
*/ */
void init_sinus(double l, double n) { void init_sinus(double l, double n) {
for( int i = 0; i < steps; i++ ) for( int i = 0; i < steps; i++ )
...@@ -21,7 +21,7 @@ void init_sinus(double l, double n) { ...@@ -21,7 +21,7 @@ void init_sinus(double l, double n) {
} }
/* /*
* * Fill the string with a plucked shape.
*/ */
void init_plucked(double l, double xp) { void init_plucked(double l, double xp) {
double x; double x;
...@@ -39,12 +39,16 @@ void init_plucked(double l, double xp) { ...@@ -39,12 +39,16 @@ void init_plucked(double l, double xp) {
#ifdef VERBOSE #ifdef VERBOSE
#define PRINT_FORMAT "%e" #define PRINT_FORMAT "%e"
void print(int index) { /*
* Print all calculated values in the string of one of the three currently
* known time states.
*/
void print(int state) {
for( int i = 0; i < steps; i++ ) { for( int i = 0; i < steps; i++ ) {
if( i ) if( i )
printf(" "); printf(" ");
printf(PRINT_FORMAT, y[index][i]); printf(PRINT_FORMAT, y[state][i]);
} }
puts(""); puts("");
...@@ -52,7 +56,8 @@ void print(int index) { ...@@ -52,7 +56,8 @@ void print(int index) {
#endif #endif
/* /*
* * Calculate and optionally print the string states for the specified number of
* time steps.
*/ */
void calculate_steps(int time, double tau) { void calculate_steps(int time, double tau) {
double x; double x;
...@@ -99,6 +104,9 @@ void calculate_steps(int time, double tau) { ...@@ -99,6 +104,9 @@ void calculate_steps(int time, double tau) {
} }
} }
/*
* Main method, initiates the program.
*/
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
int time, i; int time, i;
void (*init_method)(double, double); void (*init_method)(double, double);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment