220 lines
6.2 KiB
C++
220 lines
6.2 KiB
C++
|
// 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);
|
||
|
}
|
||
|
}
|
||
|
|