1 |
|
---|
2 | // g++-4.4 bench_gemm.cpp -I .. -O2 -DNDEBUG -lrt -fopenmp && OMP_NUM_THREADS=2 ./a.out
|
---|
3 | // icpc bench_gemm.cpp -I .. -O3 -DNDEBUG -lrt -openmp && OMP_NUM_THREADS=2 ./a.out
|
---|
4 |
|
---|
5 | #include <iostream>
|
---|
6 | #include <Eigen/Core>
|
---|
7 | #include <bench/BenchTimer.h>
|
---|
8 |
|
---|
9 | using namespace std;
|
---|
10 | using namespace Eigen;
|
---|
11 |
|
---|
12 | #ifndef SCALAR
|
---|
13 | // #define SCALAR std::complex<float>
|
---|
14 | #define SCALAR float
|
---|
15 | #endif
|
---|
16 |
|
---|
17 | typedef SCALAR Scalar;
|
---|
18 | typedef NumTraits<Scalar>::Real RealScalar;
|
---|
19 | typedef Matrix<RealScalar,Dynamic,Dynamic> A;
|
---|
20 | typedef Matrix</*Real*/Scalar,Dynamic,Dynamic> B;
|
---|
21 | typedef Matrix<Scalar,Dynamic,Dynamic> C;
|
---|
22 | typedef Matrix<RealScalar,Dynamic,Dynamic> M;
|
---|
23 |
|
---|
24 | #ifdef HAVE_BLAS
|
---|
25 |
|
---|
26 | extern "C" {
|
---|
27 | #include <Eigen/src/misc/blas.h>
|
---|
28 | }
|
---|
29 |
|
---|
30 | static float fone = 1;
|
---|
31 | static float fzero = 0;
|
---|
32 | static double done = 1;
|
---|
33 | static double szero = 0;
|
---|
34 | static std::complex<float> cfone = 1;
|
---|
35 | static std::complex<float> cfzero = 0;
|
---|
36 | static std::complex<double> cdone = 1;
|
---|
37 | static std::complex<double> cdzero = 0;
|
---|
38 | static char notrans = 'N';
|
---|
39 | static char trans = 'T';
|
---|
40 | static char nonunit = 'N';
|
---|
41 | static char lower = 'L';
|
---|
42 | static char right = 'R';
|
---|
43 | static int intone = 1;
|
---|
44 |
|
---|
45 | void blas_gemm(const MatrixXf& a, const MatrixXf& b, MatrixXf& c)
|
---|
46 | {
|
---|
47 | int M = c.rows(); int N = c.cols(); int K = a.cols();
|
---|
48 | int lda = a.rows(); int ldb = b.rows(); int ldc = c.rows();
|
---|
49 |
|
---|
50 | sgemm_(¬rans,¬rans,&M,&N,&K,&fone,
|
---|
51 | const_cast<float*>(a.data()),&lda,
|
---|
52 | const_cast<float*>(b.data()),&ldb,&fone,
|
---|
53 | c.data(),&ldc);
|
---|
54 | }
|
---|
55 |
|
---|
56 | EIGEN_DONT_INLINE void blas_gemm(const MatrixXd& a, const MatrixXd& b, MatrixXd& c)
|
---|
57 | {
|
---|
58 | int M = c.rows(); int N = c.cols(); int K = a.cols();
|
---|
59 | int lda = a.rows(); int ldb = b.rows(); int ldc = c.rows();
|
---|
60 |
|
---|
61 | dgemm_(¬rans,¬rans,&M,&N,&K,&done,
|
---|
62 | const_cast<double*>(a.data()),&lda,
|
---|
63 | const_cast<double*>(b.data()),&ldb,&done,
|
---|
64 | c.data(),&ldc);
|
---|
65 | }
|
---|
66 |
|
---|
67 | void blas_gemm(const MatrixXcf& a, const MatrixXcf& b, MatrixXcf& c)
|
---|
68 | {
|
---|
69 | int M = c.rows(); int N = c.cols(); int K = a.cols();
|
---|
70 | int lda = a.rows(); int ldb = b.rows(); int ldc = c.rows();
|
---|
71 |
|
---|
72 | cgemm_(¬rans,¬rans,&M,&N,&K,(float*)&cfone,
|
---|
73 | const_cast<float*>((const float*)a.data()),&lda,
|
---|
74 | const_cast<float*>((const float*)b.data()),&ldb,(float*)&cfone,
|
---|
75 | (float*)c.data(),&ldc);
|
---|
76 | }
|
---|
77 |
|
---|
78 | void blas_gemm(const MatrixXcd& a, const MatrixXcd& b, MatrixXcd& c)
|
---|
79 | {
|
---|
80 | int M = c.rows(); int N = c.cols(); int K = a.cols();
|
---|
81 | int lda = a.rows(); int ldb = b.rows(); int ldc = c.rows();
|
---|
82 |
|
---|
83 | zgemm_(¬rans,¬rans,&M,&N,&K,(double*)&cdone,
|
---|
84 | const_cast<double*>((const double*)a.data()),&lda,
|
---|
85 | const_cast<double*>((const double*)b.data()),&ldb,(double*)&cdone,
|
---|
86 | (double*)c.data(),&ldc);
|
---|
87 | }
|
---|
88 |
|
---|
89 |
|
---|
90 |
|
---|
91 | #endif
|
---|
92 |
|
---|
93 | void matlab_cplx_cplx(const M& ar, const M& ai, const M& br, const M& bi, M& cr, M& ci)
|
---|
94 | {
|
---|
95 | cr.noalias() += ar * br;
|
---|
96 | cr.noalias() -= ai * bi;
|
---|
97 | ci.noalias() += ar * bi;
|
---|
98 | ci.noalias() += ai * br;
|
---|
99 | }
|
---|
100 |
|
---|
101 | void matlab_real_cplx(const M& a, const M& br, const M& bi, M& cr, M& ci)
|
---|
102 | {
|
---|
103 | cr.noalias() += a * br;
|
---|
104 | ci.noalias() += a * bi;
|
---|
105 | }
|
---|
106 |
|
---|
107 | void matlab_cplx_real(const M& ar, const M& ai, const M& b, M& cr, M& ci)
|
---|
108 | {
|
---|
109 | cr.noalias() += ar * b;
|
---|
110 | ci.noalias() += ai * b;
|
---|
111 | }
|
---|
112 |
|
---|
113 | template<typename A, typename B, typename C>
|
---|
114 | EIGEN_DONT_INLINE void gemm(const A& a, const B& b, C& c)
|
---|
115 | {
|
---|
116 | c.noalias() += a * b;
|
---|
117 | }
|
---|
118 |
|
---|
119 | int main(int argc, char ** argv)
|
---|
120 | {
|
---|
121 | std::ptrdiff_t l1 = internal::queryL1CacheSize();
|
---|
122 | std::ptrdiff_t l2 = internal::queryTopLevelCacheSize();
|
---|
123 | std::cout << "L1 cache size = " << (l1>0 ? l1/1024 : -1) << " KB\n";
|
---|
124 | std::cout << "L2/L3 cache size = " << (l2>0 ? l2/1024 : -1) << " KB\n";
|
---|
125 | typedef internal::gebp_traits<Scalar,Scalar> Traits;
|
---|
126 | std::cout << "Register blocking = " << Traits::mr << " x " << Traits::nr << "\n";
|
---|
127 |
|
---|
128 | int rep = 1; // number of repetitions per try
|
---|
129 | int tries = 2; // number of tries, we keep the best
|
---|
130 |
|
---|
131 | int s = 2048;
|
---|
132 | int cache_size = -1;
|
---|
133 |
|
---|
134 | bool need_help = false;
|
---|
135 | for (int i=1; i<argc; ++i)
|
---|
136 | {
|
---|
137 | if(argv[i][0]=='s')
|
---|
138 | s = atoi(argv[i]+1);
|
---|
139 | else if(argv[i][0]=='c')
|
---|
140 | cache_size = atoi(argv[i]+1);
|
---|
141 | else if(argv[i][0]=='t')
|
---|
142 | tries = atoi(argv[i]+1);
|
---|
143 | else if(argv[i][0]=='p')
|
---|
144 | rep = atoi(argv[i]+1);
|
---|
145 | else
|
---|
146 | need_help = true;
|
---|
147 | }
|
---|
148 |
|
---|
149 | if(need_help)
|
---|
150 | {
|
---|
151 | std::cout << argv[0] << " s<matrix size> c<cache size> t<nb tries> p<nb repeats>\n";
|
---|
152 | return 1;
|
---|
153 | }
|
---|
154 |
|
---|
155 | if(cache_size>0)
|
---|
156 | setCpuCacheSizes(cache_size,96*cache_size);
|
---|
157 |
|
---|
158 | int m = s;
|
---|
159 | int n = s;
|
---|
160 | int p = s;
|
---|
161 | A a(m,p); a.setRandom();
|
---|
162 | B b(p,n); b.setRandom();
|
---|
163 | C c(m,n); c.setOnes();
|
---|
164 | C rc = c;
|
---|
165 |
|
---|
166 | std::cout << "Matrix sizes = " << m << "x" << p << " * " << p << "x" << n << "\n";
|
---|
167 | std::ptrdiff_t mc(m), nc(n), kc(p);
|
---|
168 | internal::computeProductBlockingSizes<Scalar,Scalar>(kc, mc, nc);
|
---|
169 | std::cout << "blocking size (mc x kc) = " << mc << " x " << kc << "\n";
|
---|
170 |
|
---|
171 | C r = c;
|
---|
172 |
|
---|
173 | // check the parallel product is correct
|
---|
174 | #if defined EIGEN_HAS_OPENMP
|
---|
175 | int procs = omp_get_max_threads();
|
---|
176 | if(procs>1)
|
---|
177 | {
|
---|
178 | #ifdef HAVE_BLAS
|
---|
179 | blas_gemm(a,b,r);
|
---|
180 | #else
|
---|
181 | omp_set_num_threads(1);
|
---|
182 | r.noalias() += a * b;
|
---|
183 | omp_set_num_threads(procs);
|
---|
184 | #endif
|
---|
185 | c.noalias() += a * b;
|
---|
186 | if(!r.isApprox(c)) std::cerr << "Warning, your parallel product is crap!\n\n";
|
---|
187 | }
|
---|
188 | #elif defined HAVE_BLAS
|
---|
189 | blas_gemm(a,b,r);
|
---|
190 | c.noalias() += a * b;
|
---|
191 | if(!r.isApprox(c)) std::cerr << "Warning, your product is crap!\n\n";
|
---|
192 | #else
|
---|
193 | gemm(a,b,c);
|
---|
194 | r.noalias() += a.cast<Scalar>() * b.cast<Scalar>();
|
---|
195 | if(!r.isApprox(c)) std::cerr << "Warning, your product is crap!\n\n";
|
---|
196 | #endif
|
---|
197 |
|
---|
198 | #ifdef HAVE_BLAS
|
---|
199 | BenchTimer tblas;
|
---|
200 | c = rc;
|
---|
201 | BENCH(tblas, tries, rep, blas_gemm(a,b,c));
|
---|
202 | std::cout << "blas cpu " << tblas.best(CPU_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/tblas.best(CPU_TIMER))*1e-9 << " GFLOPS \t(" << tblas.total(CPU_TIMER) << "s)\n";
|
---|
203 | std::cout << "blas real " << tblas.best(REAL_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/tblas.best(REAL_TIMER))*1e-9 << " GFLOPS \t(" << tblas.total(REAL_TIMER) << "s)\n";
|
---|
204 | #endif
|
---|
205 |
|
---|
206 | BenchTimer tmt;
|
---|
207 | c = rc;
|
---|
208 | BENCH(tmt, tries, rep, gemm(a,b,c));
|
---|
209 | std::cout << "eigen cpu " << tmt.best(CPU_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/tmt.best(CPU_TIMER))*1e-9 << " GFLOPS \t(" << tmt.total(CPU_TIMER) << "s)\n";
|
---|
210 | std::cout << "eigen real " << tmt.best(REAL_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/tmt.best(REAL_TIMER))*1e-9 << " GFLOPS \t(" << tmt.total(REAL_TIMER) << "s)\n";
|
---|
211 |
|
---|
212 | #ifdef EIGEN_HAS_OPENMP
|
---|
213 | if(procs>1)
|
---|
214 | {
|
---|
215 | BenchTimer tmono;
|
---|
216 | omp_set_num_threads(1);
|
---|
217 | Eigen::internal::setNbThreads(1);
|
---|
218 | c = rc;
|
---|
219 | BENCH(tmono, tries, rep, gemm(a,b,c));
|
---|
220 | std::cout << "eigen mono cpu " << tmono.best(CPU_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/tmono.best(CPU_TIMER))*1e-9 << " GFLOPS \t(" << tmono.total(CPU_TIMER) << "s)\n";
|
---|
221 | std::cout << "eigen mono real " << tmono.best(REAL_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/tmono.best(REAL_TIMER))*1e-9 << " GFLOPS \t(" << tmono.total(REAL_TIMER) << "s)\n";
|
---|
222 | std::cout << "mt speed up x" << tmono.best(CPU_TIMER) / tmt.best(REAL_TIMER) << " => " << (100.0*tmono.best(CPU_TIMER) / tmt.best(REAL_TIMER))/procs << "%\n";
|
---|
223 | }
|
---|
224 | #endif
|
---|
225 |
|
---|
226 | #ifdef DECOUPLED
|
---|
227 | if((NumTraits<A::Scalar>::IsComplex) && (NumTraits<B::Scalar>::IsComplex))
|
---|
228 | {
|
---|
229 | M ar(m,p); ar.setRandom();
|
---|
230 | M ai(m,p); ai.setRandom();
|
---|
231 | M br(p,n); br.setRandom();
|
---|
232 | M bi(p,n); bi.setRandom();
|
---|
233 | M cr(m,n); cr.setRandom();
|
---|
234 | M ci(m,n); ci.setRandom();
|
---|
235 |
|
---|
236 | BenchTimer t;
|
---|
237 | BENCH(t, tries, rep, matlab_cplx_cplx(ar,ai,br,bi,cr,ci));
|
---|
238 | std::cout << "\"matlab\" cpu " << t.best(CPU_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/t.best(CPU_TIMER))*1e-9 << " GFLOPS \t(" << t.total(CPU_TIMER) << "s)\n";
|
---|
239 | std::cout << "\"matlab\" real " << t.best(REAL_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/t.best(REAL_TIMER))*1e-9 << " GFLOPS \t(" << t.total(REAL_TIMER) << "s)\n";
|
---|
240 | }
|
---|
241 | if((!NumTraits<A::Scalar>::IsComplex) && (NumTraits<B::Scalar>::IsComplex))
|
---|
242 | {
|
---|
243 | M a(m,p); a.setRandom();
|
---|
244 | M br(p,n); br.setRandom();
|
---|
245 | M bi(p,n); bi.setRandom();
|
---|
246 | M cr(m,n); cr.setRandom();
|
---|
247 | M ci(m,n); ci.setRandom();
|
---|
248 |
|
---|
249 | BenchTimer t;
|
---|
250 | BENCH(t, tries, rep, matlab_real_cplx(a,br,bi,cr,ci));
|
---|
251 | std::cout << "\"matlab\" cpu " << t.best(CPU_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/t.best(CPU_TIMER))*1e-9 << " GFLOPS \t(" << t.total(CPU_TIMER) << "s)\n";
|
---|
252 | std::cout << "\"matlab\" real " << t.best(REAL_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/t.best(REAL_TIMER))*1e-9 << " GFLOPS \t(" << t.total(REAL_TIMER) << "s)\n";
|
---|
253 | }
|
---|
254 | if((NumTraits<A::Scalar>::IsComplex) && (!NumTraits<B::Scalar>::IsComplex))
|
---|
255 | {
|
---|
256 | M ar(m,p); ar.setRandom();
|
---|
257 | M ai(m,p); ai.setRandom();
|
---|
258 | M b(p,n); b.setRandom();
|
---|
259 | M cr(m,n); cr.setRandom();
|
---|
260 | M ci(m,n); ci.setRandom();
|
---|
261 |
|
---|
262 | BenchTimer t;
|
---|
263 | BENCH(t, tries, rep, matlab_cplx_real(ar,ai,b,cr,ci));
|
---|
264 | std::cout << "\"matlab\" cpu " << t.best(CPU_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/t.best(CPU_TIMER))*1e-9 << " GFLOPS \t(" << t.total(CPU_TIMER) << "s)\n";
|
---|
265 | std::cout << "\"matlab\" real " << t.best(REAL_TIMER)/rep << "s \t" << (double(m)*n*p*rep*2/t.best(REAL_TIMER))*1e-9 << " GFLOPS \t(" << t.total(REAL_TIMER) << "s)\n";
|
---|
266 | }
|
---|
267 | #endif
|
---|
268 |
|
---|
269 | return 0;
|
---|
270 | }
|
---|
271 |
|
---|