Benchmarks
Ziele:
-
Erzeugen einfacher Benchmarks, um die Laufzeit verschiedener numerischer Verfahren zu vergleichen.
-
Mit Macros sollen Paramter für das Problem beim Compilieren festgelgt werden können. Damit können mit einem cleveren Makefile verschiedene Konfigurationen getestet werden.
-
Beim Bechmark wird nicht nur die Laufzeit vergleichen, sondern auch getestet, ob alle Varianten (numerisch) das gleiche Ergebnis produzieren.
-
Die Ausgabe ist eine Tabelle, die in eine Datei für das Post-Processing umgelenkt werden kann.
-
Mit Programmen wie GNUPlot kann das Ergebnis visualisiert werden.
Beispiel aus der Vorlesung
Wie in der Vorlesung gezeigt, wird in folgendem Programm die Walltime verglichen, die benötigt wird, um Matrizen die zeilen- oder spaltenweise im Speicher liegen zu initalisieren.
Weitere Details:
-
Für jede Variante wird das Ergebnis nach der Durchführung in einen eigenen Puffer (buffer1 und buffer2 kopiert). Bei der Berechnung arbeitet jede Variante aber auf demselben Puffer buffer.
-
Mit asumDiff wird verglichen, ob die Puffer (numerisch) das gleiche Ergebnis beinhalten.
#include <stdio.h> #include <stdlib.h> #include <sys/times.h> #include <unistd.h> #include <math.h> #ifndef MINDIM_M #define MINDIM_M 1000 #endif #ifndef MINDIM_N #define MINDIM_N 1000 #endif #ifndef MINDIM_K #define MINDIM_K 1000 #endif #ifndef MAXDIM_M #define MAXDIM_M 5000 #endif #ifndef MAXDIM_N #define MAXDIM_N 5000 #endif #ifndef MAXDIM_K #define MAXDIM_K 5000 #endif #ifndef INC_M #define INC_M 100 #endif #ifndef INC_N #define INC_N 100 #endif #ifndef INC_K #define INC_K 100 #endif #ifndef MIN_T #define MIN_T 1 #endif double buffer[MAXDIM_M*MAXDIM_N]; double buffer1[MAXDIM_M*MAXDIM_N]; double buffer2[MAXDIM_M*MAXDIM_N]; double walltime() { struct tms ts; static double ClockTick=0.0; if (ClockTick==0.0) { ClockTick = 1.0 / ((double) sysconf(_SC_CLK_TCK)); } return ((double) times(&ts)) * ClockTick; } void gecopy(long m, long n, const double *A, long incRowA, long incColA, double *B, long incRowB, long incColB) { long i, j; for (i=0; i<m; ++i) { for (j=0; j<n; ++j) { B[i*incRowB+j*incColB] = A[i*incRowA+j*incColA]; } } } double asumDiff(long m, long n, const double *A, long incRowA, long incColA, const double *B, long incRowB, long incColB) { double diff = 0; long i, j; for (i=0; i<m; ++i) { for (j=0; j<n; ++j) { diff += fabs(B[i*incRowB+j*incColB] - A[i*incRowA+j*incColA]); } } return diff; } //------------------------------------------------------------------------------ void initMatrix(long m, long n, double *A, long incRowA, long incColA) { long i, j; for (j=0; j<n; ++j) { for (i=0; i<m; ++i) { A[i*incRowA+j*incColA] = j*n+i+1; } } } void printMatrix(long m, long n, const double *A, long incRowA, long incColA) { long i, j; for (i=0; i<m; ++i) { printf(" "); for (j=0; j<n; ++j) { printf("%4.1lf ", A[i*incRowA+j*incColA]); } printf("\n"); } printf("\n"); } int main() { long m, n, runs; double t0, t1, t2, diff; printf("# M N t1 (colMajor) t2 (rowMajor) t2/t1 diff\n"); printf("#================================================================\n"); for (m=MINDIM_M, n=MINDIM_N; m<=MAXDIM_M && n<MAXDIM_N; m+=INC_M, n+=INC_N) { printf("%4ld %4ld ", m, n); runs = 0; t1 = 0; do { t0 = walltime(); initMatrix(m, n, buffer, 1, m); t1 += walltime() - t0; ++runs; } while (t1<MIN_T); t1 /= runs; gecopy(m, n, buffer, 1, m, buffer1, 1, m); printf(" %12.2lf", t1); runs = 0; t2 = 0; do { t0 = walltime(); initMatrix(m, n, buffer, n, 1); t2 += walltime() - t0; ++runs; } while (t2<MIN_T); t2 /= runs; gecopy(m, n, buffer, n, 1, buffer2, n, 1); diff = asumDiff(m, n, buffer1, 1, m, buffer2, n, 1); printf(" %12.2lf %16.2lf", t2, t2/t1); printf(" %11.2le", diff); printf("\n"); } return 0; }
Übersetzen und Ausführen
Hier wurde der Paramter MIN_T beim Übersetzen auf 0 gesetzt, um das Erzeugen dieser Seite zu bescgleunigen.
$shell> gcc -Wall -DMIN_T=0 -o bench_initmatrix bench_initmatrix.c $shell> ./bench_initmatrix # M N t1 (colMajor) t2 (rowMajor) t2/t1 diff #================================================================ 1000 1000 0.01 0.01 1.00 0.00e+00 1100 1100 0.01 0.02 2.00 0.00e+00 1200 1200 0.01 0.02 2.00 0.00e+00 1300 1300 0.01 0.03 3.00 0.00e+00 1400 1400 0.01 0.03 3.00 0.00e+00 1500 1500 0.01 0.03 3.00 0.00e+00 1600 1600 0.01 0.06 6.00 0.00e+00 1700 1700 0.01 0.05 5.00 0.00e+00 1800 1800 0.02 0.05 2.50 0.00e+00 1900 1900 0.02 0.06 3.00 0.00e+00 2000 2000 0.02 0.07 3.50 0.00e+00 2100 2100 0.02 0.07 3.50 0.00e+00 2200 2200 0.03 0.08 2.67 0.00e+00 2300 2300 0.03 0.09 3.00 0.00e+00 2400 2400 0.04 0.13 3.25 0.00e+00 2500 2500 0.04 0.11 2.75 0.00e+00 2600 2600 0.04 0.12 3.00 0.00e+00 2700 2700 0.04 0.12 3.00 0.00e+00 2800 2800 0.04 0.18 4.50 0.00e+00 2900 2900 0.04 0.14 3.50 0.00e+00 3000 3000 0.05 0.16 3.20 0.00e+00 3100 3100 0.05 0.16 3.20 0.00e+00 3200 3200 0.06 0.24 4.00 0.00e+00 3300 3300 0.06 0.19 3.17 0.00e+00 3400 3400 0.06 0.20 3.33 0.00e+00 3500 3500 0.06 0.21 3.50 0.00e+00 3600 3600 0.08 0.30 3.75 0.00e+00 3700 3700 0.07 0.24 3.43 0.00e+00 3800 3800 0.08 0.26 3.25 0.00e+00 3900 3900 0.09 0.27 3.00 0.00e+00 4000 4000 0.08 0.38 4.75 0.00e+00 4100 4100 0.09 0.30 3.33 0.00e+00 4200 4200 0.10 0.35 3.50 0.00e+00 4300 4300 0.10 0.37 3.70 0.00e+00 4400 4400 0.10 0.46 4.60 0.00e+00 4500 4500 0.11 0.44 4.00 0.00e+00 4600 4600 0.11 0.50 4.55 0.00e+00 4700 4700 0.12 0.49 4.08 0.00e+00 4800 4800 0.12 0.54 4.50 0.00e+00 4900 4900 0.13 0.56 4.31 0.00e+00
Benchmark durchführen
Wir zeigen zunächst an einem Beispiel, wie man generell Plots für Benchmarks macht. Darauf aufbauend sollt ihr eigene Plots zu anderen Charakteristiken (Speedup) und Darstellungsformen (Log-Skalierung) erstellen. Die Aufgabenstellung folgt am Ende des Abschnittes.
Ausgabe in Datei lenken
Da die Ausgabe mit printf alles nach stdout schreibt, können wir diese einfach in eine Datei umlenken:
$shell> ./bench_initmatrix > report.initmatrix
GNUPlot Skript schreiben
Man kann GNUPlot auch interaktiv bedienen (zeigen wir). Aber genau so wichtig ist es, dass man dafür Skripte schreiben kann, um aus Text-Tabellen Plots erzeugen zu können.
Mit folgendem GNUPlot-Skript werden Plots erstellt, die zeigen wie die Laufzeit vom Speicherformat abhängt:
set terminal svg size 900, 500 set output "bench.initmatrix.svg" set xlabel "Matrix dim A: M=N" set title "Matrix-Initialisierung" set key outside set pointsize 0.5 plot "report.initmatrix" using 1:3 with linespoints lt 2 lw 3 title "Col-Major", \ "report.initmatrix" using 1:4 with linespoints lt 3 lw 3 title "Row-Major"
Plots erstellen
$shell> gnuplot plot.initmatrix
Aufgabe
-
Y-Achse beschriften
-
Fontsize für Titel und Achsenbeschriftung ändern
-
Logarithmische Darstellung für X- und Y-Achse. Wann und wieso ist das sinnvoll?
-
Neuer Plot für Speedup. Welche Skalierung ist sinnvoll?