|
|
// g++ -O3 -DNDEBUG -I.. -L /usr/lib64/atlas/ benchBlasGemm.cpp -o benchBlasGemm -lrt -lcblas
// possible options:
// -DEIGEN_DONT_VECTORIZE
// -msse2
// #define EIGEN_DEFAULT_TO_ROW_MAJOR
#define _FLOAT
#include <iostream>
#include <Eigen/Core>
#include "BenchTimer.h"
// include the BLAS headers
extern "C" { #include <cblas.h>
} #include <string>
#ifdef _FLOAT
typedef float Scalar; #define CBLAS_GEMM cblas_sgemm
#else
typedef double Scalar; #define CBLAS_GEMM cblas_dgemm
#endif
typedef Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic> MyMatrix; void bench_eigengemm(MyMatrix& mc, const MyMatrix& ma, const MyMatrix& mb, int nbloops); void check_product(int M, int N, int K); void check_product(void);
int main(int argc, char *argv[]) { // disable SSE exceptions
#ifdef __GNUC__
{ int aux; asm( "stmxcsr %[aux] \n\t" "orl $32832, %[aux] \n\t" "ldmxcsr %[aux] \n\t" : : [aux] "m" (aux)); } #endif
int nbtries=1, nbloops=1, M, N, K;
if (argc==2) { if (std::string(argv[1])=="check") check_product(); else M = N = K = atoi(argv[1]); } else if ((argc==3) && (std::string(argv[1])=="auto")) { M = N = K = atoi(argv[2]); nbloops = 1000000000/(M*M*M); if (nbloops<1) nbloops = 1; nbtries = 6; } else if (argc==4) { M = N = K = atoi(argv[1]); nbloops = atoi(argv[2]); nbtries = atoi(argv[3]); } else if (argc==6) { M = atoi(argv[1]); N = atoi(argv[2]); K = atoi(argv[3]); nbloops = atoi(argv[4]); nbtries = atoi(argv[5]); } else { std::cout << "Usage: " << argv[0] << " size \n"; std::cout << "Usage: " << argv[0] << " auto size\n"; std::cout << "Usage: " << argv[0] << " size nbloops nbtries\n"; std::cout << "Usage: " << argv[0] << " M N K nbloops nbtries\n"; std::cout << "Usage: " << argv[0] << " check\n"; std::cout << "Options:\n"; std::cout << " size unique size of the 2 matrices (integer)\n"; std::cout << " auto automatically set the number of repetitions and tries\n"; std::cout << " nbloops number of times the GEMM routines is executed\n"; std::cout << " nbtries number of times the loop is benched (return the best try)\n"; std::cout << " M N K sizes of the matrices: MxN = MxK * KxN (integers)\n"; std::cout << " check check eigen product using cblas as a reference\n"; exit(1); }
double nbmad = double(M) * double(N) * double(K) * double(nbloops);
if (!(std::string(argv[1])=="auto")) std::cout << M << " x " << N << " x " << K << "\n";
Scalar alpha, beta; MyMatrix ma(M,K), mb(K,N), mc(M,N); ma = MyMatrix::Random(M,K); mb = MyMatrix::Random(K,N); mc = MyMatrix::Random(M,N);
Eigen::BenchTimer timer;
// we simply compute c += a*b, so:
alpha = 1; beta = 1;
// bench cblas
// ROWS_A, COLS_B, COLS_A, 1.0, A, COLS_A, B, COLS_B, 0.0, C, COLS_B);
if (!(std::string(argv[1])=="auto")) { timer.reset(); for (uint k=0 ; k<nbtries ; ++k) { timer.start(); for (uint j=0 ; j<nbloops ; ++j) #ifdef EIGEN_DEFAULT_TO_ROW_MAJOR
CBLAS_GEMM(CblasRowMajor, CblasNoTrans, CblasNoTrans, M, N, K, alpha, ma.data(), K, mb.data(), N, beta, mc.data(), N); #else
CBLAS_GEMM(CblasColMajor, CblasNoTrans, CblasNoTrans, M, N, K, alpha, ma.data(), M, mb.data(), K, beta, mc.data(), M); #endif
timer.stop(); } if (!(std::string(argv[1])=="auto")) std::cout << "cblas: " << timer.value() << " (" << 1e-3*floor(1e-6*nbmad/timer.value()) << " GFlops/s)\n"; else std::cout << M << " : " << timer.value() << " ; " << 1e-3*floor(1e-6*nbmad/timer.value()) << "\n"; }
// clear
ma = MyMatrix::Random(M,K); mb = MyMatrix::Random(K,N); mc = MyMatrix::Random(M,N);
// eigen
// if (!(std::string(argv[1])=="auto"))
{ timer.reset(); for (uint k=0 ; k<nbtries ; ++k) { timer.start(); bench_eigengemm(mc, ma, mb, nbloops); timer.stop(); } if (!(std::string(argv[1])=="auto")) std::cout << "eigen : " << timer.value() << " (" << 1e-3*floor(1e-6*nbmad/timer.value()) << " GFlops/s)\n"; else std::cout << M << " : " << timer.value() << " ; " << 1e-3*floor(1e-6*nbmad/timer.value()) << "\n"; }
std::cout << "l1: " << Eigen::l1CacheSize() << std::endl; std::cout << "l2: " << Eigen::l2CacheSize() << std::endl;
return 0; }
using namespace Eigen;
void bench_eigengemm(MyMatrix& mc, const MyMatrix& ma, const MyMatrix& mb, int nbloops) { for (uint j=0 ; j<nbloops ; ++j) mc.noalias() += ma * mb; }
#define MYVERIFY(A,M) if (!(A)) { \
std::cout << "FAIL: " << M << "\n"; \ } void check_product(int M, int N, int K) { MyMatrix ma(M,K), mb(K,N), mc(M,N), maT(K,M), mbT(N,K), meigen(M,N), mref(M,N); ma = MyMatrix::Random(M,K); mb = MyMatrix::Random(K,N); maT = ma.transpose(); mbT = mb.transpose(); mc = MyMatrix::Random(M,N);
MyMatrix::Scalar eps = 1e-4;
meigen = mref = mc; CBLAS_GEMM(CblasColMajor, CblasNoTrans, CblasNoTrans, M, N, K, 1, ma.data(), M, mb.data(), K, 1, mref.data(), M); meigen += ma * mb; MYVERIFY(meigen.isApprox(mref, eps),". * .");
meigen = mref = mc; CBLAS_GEMM(CblasColMajor, CblasTrans, CblasNoTrans, M, N, K, 1, maT.data(), K, mb.data(), K, 1, mref.data(), M); meigen += maT.transpose() * mb; MYVERIFY(meigen.isApprox(mref, eps),"T * .");
meigen = mref = mc; CBLAS_GEMM(CblasColMajor, CblasTrans, CblasTrans, M, N, K, 1, maT.data(), K, mbT.data(), N, 1, mref.data(), M); meigen += (maT.transpose()) * (mbT.transpose()); MYVERIFY(meigen.isApprox(mref, eps),"T * T");
meigen = mref = mc; CBLAS_GEMM(CblasColMajor, CblasNoTrans, CblasTrans, M, N, K, 1, ma.data(), M, mbT.data(), N, 1, mref.data(), M); meigen += ma * mbT.transpose(); MYVERIFY(meigen.isApprox(mref, eps),". * T"); }
void check_product(void) { int M, N, K; for (uint i=0; i<1000; ++i) { M = internal::random<int>(1,64); N = internal::random<int>(1,768); K = internal::random<int>(1,768); M = (0 + M) * 1; std::cout << M << " x " << N << " x " << K << "\n"; check_product(M, N, K); } }
|