==================================== GEMM für unterschiedliche Datentypen [TOC] ==================================== Die Referenz-Implementierung der GEMM Operation zu verallgemeinern ist relativ einfach. Dies geschieht analog zu den vorigen Beispielen `gecopy`, `geaxpy` und `gescal`. Lediglich die Anzahl der Template-Parameter erhöht sich: ---- CODE (type=cc) ------------------------------------------------------------ namespace refblas { template void gemm(Index m, Index n, Index k, Alpha alpha, const TA *A, Index incRowA, Index incColA, const TB *B, Index incRowB, Index incColB, Beta beta, TC *C, Index incRowC, Index incColC) { hpc::ulmblas::gescal(m, n, beta, C, incRowC, incColC); for (Index j=0; j doc:session10/page06 Festlegung von Blockgrößen durch Macros ======================================= Wir verwenden Macros, um für Blockgößen Default-Werte festzulegen, die wir aber beim Übersetzen eventuell abändern können. Dies geschieht nach dem Muster ---- CODE (type=cc) ------------------------------------------------------------ // // double precision(double) // #ifndef D_BLOCKSIZE_MR #define D_BLOCKSIZE_MR 4 #endif #ifndef D_BLOCKSIZE_NR #define D_BLOCKSIZE_NR 4 #endif #ifndef D_BLOCKSIZE_MC #define D_BLOCKSIZE_MC 256 #endif #ifndef D_BLOCKSIZE_KC #define D_BLOCKSIZE_KC 256 #endif #ifndef D_BLOCKSIZE_NC #define D_BLOCKSIZE_NC 4096 #endif -------------------------------------------------------------------------------- Dabei steht `D` für `double`. Analog benutzen wir gemäß der BLAS Konvention Macros mit Prefix `S` für `float`, `C` für `std::complex` und `Z` für `std::complex`. Für andere Datentypen legen wir gemeinsame Default-Werte mit Prefix `DEFAULT` fest. Aufgabe ------- Übernehmt das fertige __config.h__ und kopiert es in `hpc/ulmblas`. :links: config.h -> file:session12/page01/hpc/ulmblas/config.h Festlegung von Blockgrößen durch Traits ======================================= Die oben definierten Macro verwenden wir wieder indirekt mit Hilfe einer Traits-Klasse. Damit kann die Blockgröße in Abhängigkeit eines Template-Parameter zur Compile-Zeit festgelegt werden: ---- CODE (type=cc) ------------------------------------------------------------ template void foo(Index m, Index k, T *A) { Index MR = hpc::ulmblas::BlockSize::MR; /* ... */ } -------------------------------------------------------------------------------- Aufgabe ------- Die Traits-Klasse `hpc::ulmblas::BlockSize` packen wir in `hpc/ulmblas/blocksize.h`. Dabei sollt ihr folgende Vorlage um Spezialisierungen für `float`, `double`, `std::complex` und `std::complex` ergänzen: ---- CODE (type=cc) ------------------------------------------------------------ #ifndef HPC_ULMBLAS_BLOCKSIZE_H #define HPC_ULMBLAS_BLOCKSIZE_H 1 #include #include namespace hpc { namespace ulmblas { template struct BlockSize { static const int MC = DEFAULT_BLOCKSIZE_MC; static const int KC = DEFAULT_BLOCKSIZE_KC; static const int NC = DEFAULT_BLOCKSIZE_NC; static const int MR = DEFAULT_BLOCKSIZE_MR; static const int NR = DEFAULT_BLOCKSIZE_NR; static_assert(MC>0 && KC>0 && NC>0 && MR>0 && NR>0, "Invalid block size."); static_assert(MC % MR == 0, "MC must be a multiple of MR."); static_assert(NC % NR == 0, "NC must be a multiple of NR."); }; /* Special cases of BlockSize for float, double, ... */ } } // namespace ulmblas, hpc #endif // HPC_ULMBLAS_BLOCKSIZE_H -------------------------------------------------------------------------------- Funktionen zum Packen von Blöcken ================================= Als Vorlage zum Packen von Blöcken dient: ---- CODE (type=cc) ------------------------------------------------------------ template void pack_A(Index mc, Index kc, const T *A, Index incRowA, Index incColA, T *p) { Index MR = BlockSize::MR; Index mp = (mc+MR-1) / MR; for (Index j=0; j void pack_B(Index kc, Index nc, const TB *B, Index incRowB, Index incColB, T *p) { Index NR = BlockSize::NR; Index np = (nc+NR-1) / NR; for (Index l=0; l http://www.cplusplus.com/reference/algorithm/fill_n/ ---- CODE (type=cc) ------------------------------------------------------------ template void mgemm(Index mc, Index nc, Index kc, T alpha, const T *A, const T *B, T beta, T *C, Index incRowC, Index incColC) { Index MR = BlockSize::MR; Index NR = BlockSize::NR; T C_[BlockSize::MR*BlockSize::NR]; Index mp = (mc+MR-1) / MR; Index np = (nc+NR-1) / NR; Index mr_ = mc % MR; Index nr_ = nc % NR; for (Index j=0; j void ugemm(Index kc, T alpha, const T *A, const T *B, T beta, T *C, Index incRowC, Index incColC) { const Index MR = BlockSize::MR; const Index NR = BlockSize::NR; T P[BlockSize::MR*BlockSize::NR]; for (Index l=0; l void gemm(Index m, Index n, Index k, T alpha, const T *A, Index incRowA, Index incColA, const T *B, Index incRowB, Index incColB, T beta, T *C, Index incRowC, Index incColC) { Index MC = BlockSize::MC; Index NC = BlockSize::NC; Index KC = BlockSize::KC; Index mb = (m+MC-1) / MC; Index nb = (n+NC-1) / NC; Index kb = (k+KC-1) / KC; Index mc_ = m % MC; Index nc_ = n % NC; Index kc_ = k % KC; T *A_ = new T[MC*KC]; T *B_ = new T[KC*NC]; if (alpha==T(0) || k==0) { gescal(m, n, beta, C, incRowC, incColC); return; } for (Index j=0; j::type FooType; ------------------------------------------------------------------------------ Wie dies realisiert wird ist allerdings ein eigenes Thema ... - Das Ganze packen wir in `hpc/ulmblas/gemm.h`. Test ==== Da wir die Matrix-Klassen noch noch angepasst haben und auch andere Hilfsmittel noch übertragen werden müssen, sieht der low-level Benchmark wieder etwas umständlicher aus: :import: session12/page01/hpc/tests/gemm.cc Aufgaben -------- - Testen, ob alles tut. - Das Matrix-Produkt für verschiedene Matrix-Element Typen sowie verschieden Typen für die Skalare `alpha` und `beta` testen: `float`, `double`, `std::complex`, `std::complex`, aber auch `int` oder `long`. Manche Kombinationen werden nicht übersetzen. Hier sollte man sich jeweils Gedanken über den Grund machen. Beispiel: Wo geht es schief, wenn man als Matrix-Element Typ `int` festlegt? :navigate: up -> doc:index back -> doc:session12/page04 next -> doc:session12/page06