Matrix reloaded, Von Matrizen und magischen Compilerfähigkeiten c't 12/2014 S.176
---------------------------------------------------------------------------------- 


Projektmappe
mmtest.sln   für Microsoft Visual Studio 2013

Projekte: 
MMtest     für Intel C++ 14 
MMtest_MC  für Microsoft VS 2013
MMgen      für Microsoft VS 2013 

MMtest enthält eine Vielzahl verschiedener Versionen von Matrixmultiplikationsroutinen 
mit statischen globalen Feldern, mit typisierten Parametern oder mit Pointern, mit Dimensionen, die schon zur Compilezeit feststehen 
oder erst zu Laufzeit, in allen Reihenfolgen ijk, ikj, jik, jki, kij, kji,
mit und ohne Tiling, unterschiedlichem Nullsetzverfahren von c etc. 

Das Projekt MMtest ist für Intel C++ 14 vorbereitet mit einer Vielzahl von Konfigurationen (SSE4.2, AVX, AVX2, mit Opt-matmul etc).   

Weitere Konfigurationen sind durch Setzen von Defines möglich, etwa /Dnovec sorgt für das Abschalten der Autovektorisierung. 
/DDIM=xxx setzt die Dimension (Default 1024), /DDIM0=xxx die Leading Dimension (Default 1024) 

Das Projekt mmtest-mc bietet die gleiche Routinen, ist aber vorbereitet für den Microsoft C/C++-Compiler mit Release (nur /O2, SSE3-Code), AVX und AVX2 (im Visual Studio 2013 Update 1 noch nicht als Menüpunkt vorgesehen)  

Den einzelnen Routinen sind Kennzahlen zugeordnet. Mit "mmtestAVX.exe -b xx" wird Routine xx in AVX-Optimierung ausgeführt,    
mmtestAVX.exe -b 0 -be 100 werden alle vorhandenen Routinen ausgestoppt. Kennzahl 100 steht für den direkten Einsprung in die MKL. Um MKL und OpenMP nutzen zu können müssen die zugehörigen DDLs im Verzeichnis der auszuführenden 
Datei oder im Pfad liegen. (Bei Start aus dem Visual Studio heraus erreicht man das einfach durch Anwählen der MKL in den Intel Performance Libraries).   

Um für MKL zu übersetzen ist zusätzlich /DMKL zu setzen. Mit "-t xxxx" kann man eine Mindestlaufzeit im ms angeben. 
 
Um den Durchschnittstakt und daraus die Metrik Flops per Clock (Fpc) bei Prozessoren mit mit Turbo Mode zu bestimmen, verwendet das Programm Treiber von OpenSysLibs.org (Copyright (c) 2007-2009 OpenLibSys.org. Die Treiber sind signiert, die Programme benötigen zur ihrem Einsatz Administratorrechte. Mit /DNOMSR beim Kompilieren kann man auf die Einbindung der Treiber und die Bestimmung des Durchschnittstakts verzichten. 

Projekt MMgen ist im Zusammenspiel mit make.bat ein Generator für verschiedene Matrixroutinen. make.bat erzeugt die Include-Datei mm.h mit nur einer Routine, so lassen sich die Compiler-Reports einfach zuordnen, also insbesondere, ob der Autovektorisierer Erfolg hatte und ob er zusätzlich permutiert hat (bei Intel).    

Dazu muss man eine Konsole mit Admin-Rechten öffnen und das Projektverzeichnis mmgen öffnen. Die Compiler werden hier von der Batchdatei make.bat aufgerufen. Dazu muss man zuvor ihre Pfade und Umgebungen einrichten (mit iclvars.bat intel64 für Intel oder mit vsvars32.bat amd64 für MS C++ und so weiter). Nun kann man mit 

make <mtype> <reihenfolge>  <vektorisierung>  <compiler> <flag1> <flag2> ... das entsprechende Programm generieren und ausführen. 

Folgende Aufruftypen <mtype> kennt das Batch-Programm: 
if %mm_mtype%==0 echo void matrixmul() { >> mm.h
if %mm_mtype%==1 echo void matrixmul(int dim) { >> mm.h
if %mm_mtype%==2 echo void matrixmul(const mat a, const mat b, mat c) { >> mm.h
if %mm_mtype%==3 echo void matrixmul(const mat a, const mat b, mat c, int dim) { >> mm.h
if %mm_mtype%==11 echo void matrixmul(const double* a, const double*  b, double* c) { >> mm.h
if %mm_mtype%==12 echo void matrixmul(const double* a, const double*  b, double* c, int dim) { >> mm.h
if %mm_mtype%==13 echo void matrixmul(const double* a, const double*  b, double* c, int dim, int dim0) { >> mm.h
if %mm_mtype%==14 echo void matrixmul(const double* __restrict a, const double* __restrict b, double* __restrict c, int dim, int dim0) { >> mm.h

Die Reihenfolgen <reihenfolge> können ijk, ikj, jik, jki, kij, kji oder auch all lauten, dann werden nacheinander alle sechs erzeugt und ausgestoppt. 

<vektorisierung> ist entweder novec, auto oder vec. Letzteres setzt das pragma #SIMD (kennt Microsoft allerdings nicht).   

<compiler> ist üblicherweise cl oder icl 

<flags> je nach Wunsch 

Ein typischer Aufruf mit AVX.Optimierung sieht zum Beispiel so aus 
make 0 all auto cl /O2 /arch:AVX 
oder 
mak 0 all auto icl /O3 /Qipo /Qopt-matmul /Qopenmp

Ein Satz von Environment-Variablen steuert die Feinheiten 
mm_DIM=xxx setzt die Dimension (Default 1024) 
mm_DIM0=xxx setzt die Leading Dimension, muss >= DIM sein! 
mm_Dimname=DIM setzt die Variable auf den konstanten zur Compilezeit bekannten Wert DIM
mm_Dimname=dim setzt die Variable auf den variablen, erst zur Laufzeit bekannten Wert dim
mm_Dim0name=DIM0 setzt die Variable auf den konstanten zur Compilezeit bekannten Wert DIM0
mm_Dim0name=dim0 setzt die Variable auf den variablen, erst zur Laufzeit bekannten Wert dim0
mm_openmp=xxx setzt Openmp-Pragma vor die äußerste Schleife, Default: omp parallel for 
mm_Tiling=xx setzt Schrittweite für Tiling in der mittleren Schleife (Default=1, kein Tiling)
mm_setnull=xx setzt Nullsetzroutine, xx=setnull (default) ruft externe Routine auf, xx=inloop setzt c[i][j]=0 innerhalb der  
mittleren Schleife (geht nur bei ijk oder jik). 

Außerdem: 
make mkl parallel ... benutzt MKL parallel
make mkl sequential ... benutzt MKL sequential auf nur einem Kern

make mmAVX.h cl /O2 /arch:AVX    ... Verwendet Routine mit Microsoft Comp und AVX-Intrinsics
make mmFMA.h icl /fast           ... Verwendet Routine mit FMA-Intrinsics und Intel-Compiler 


Ein paar Beispiele:

C:\Users\cttest\Documents\Visual Studio 2013\Projects\MMtest\x64

>mmtestAVX -b 0 -be 10
=>
compiled with Intel Compiler V14.00, Build 20140130, with MSVC 18.00, C++
Windows 64, target: x86_64 Release, RTL-DLL, Multi Threaded, openmp= 201307
Options= AVX
Hochfahren
dim=1024, dim0=1024 8 MB

 0 mm_ijk()        0.990 s       5 reps, 10.85 GF/s  3.883 Fpc  2794 MHz
 1 mm_jik()        0.986 s       5 reps, 10.89 GF/s  3.897 Fpc  2794 MHz
 2 mm_kij()        0.992 s       5 reps, 10.81 GF/s  3.871 Fpc  2794 MHz
 3 mm_ikj()        0.996 s       5 reps, 10.78 GF/s  3.855 Fpc  2795 MHz
 4 mm_kji()        0.999 s       5 reps, 10.75 GF/s  3.847 Fpc  2794 MHz
 5 mm_jki()        0.983 s       5 reps, 10.91 GF/s  3.907 Fpc  2794 MHz
 6 mmX_ijk()       0.989 s       5 reps, 10.86 GF/s  3.886 Fpc  2794 MHz
 7 mmX_jik()       0.991 s       5 reps, 10.83 GF/s  3.876 Fpc  2794 MHz
10 mmF_ijk()       1.059 s       6 reps, 12.17 GF/s  4.353 Fpc  2795 MHz



C:\Users\cttest\Documents\Visual Studio 2013\Projects\MMtest\mmgen>
>make 0 ijk auto icl /fast
=>
mm.h_6, col. 2) remark: PERMUTED LOOP WAS VECTORIZED
mm.h_6, col. 2) remark: PERMUTED LOOP WAS VECTORIZED
mm.h_6, col. 2) remark: PERMUTED LOOP WAS VECTORIZED
icl mm_mul_0  /fast     i-j-k auto DIM=1024 DIM0=1024 setnull tiling=1
  1.046 s     10 reps  0.105 s/rep 20.53 GF/s =  7.348 Flops/c @2794 MHz


>set mm

mm_comp=icl
mm_DIM=1024
mm_DIM0=1024
mm_dim0name=DIM0
mm_dimname=DIM
mm_exename=mmgen
mm_exepath=..\x64
mm_flags=/fast
mm_i=i
mm_j=j
mm_k=k
mm_mtype=0
mm_runpars=-t 1000
mm_setnull=setnull
mm_tiling=1
mm_vec=auto

>make mkl parallel

icl /Qmkl:parallel /DMKL  DIM=1024 DIM0=1024
  1.061 s     68 reps 15.605 ms/rep 137.54 GF/s = 49.237 Flops/c @2794 MHz



>set mm_openmp=omp parallel for
>make 0 ijk auto icl /fast
=>
mm.h_7, col. 2) remark: PERMUTED LOOP WAS VECTORIZED
mm.h_7, col. 2) remark: PERMUTED LOOP WAS VECTORIZED
mm.h_7, col. 2) remark: PERMUTED LOOP WAS VECTORIZED
icl mm_mul_0  /fast     /Qopenmp i-j-k auto DIM=1024 DIM0=1024 setnull tiling=1
  0.970 s     33 reps 29.385 ms/rep  73.05 GF/s = 26.327 Flops/c @2775 MHz


Viel Spaß (as) 

ps. Der Einsatz der Programme und der Kerneltreiber geschieht auf eigene Gefahr. 
 



 