#ifndef TEST_COMMON_HPP_ #define TEST_COMMON_HPP_ #include #include #include #include #include "isaac/array.h" namespace sc = isaac; typedef isaac::int_t int_t; template struct numeric_trait; template<> struct numeric_trait { static constexpr float epsilon = 1e-4; static const char* name; }; template<> struct numeric_trait { static constexpr double epsilon = 1e-8; static const char* name; }; const char * numeric_trait::name = "float"; const char * numeric_trait::name = "double"; template struct BLAS; template<> struct BLAS { template static FT F(FT SAXPY, DT ) { return SAXPY; } }; template<> struct BLAS { template static DT F(FT , DT DAXPY) { return DAXPY; } }; enum interface_t { clBLAS, cuBLAS, CPP }; cl_mem cl(sc::array_base const & x) { return x.data().handle().cl(); } CUdeviceptr cu(sc::array_base const & x) { return x.data().handle().cu(); } int off(sc::array_base const & x) { return x.start(); } int inc(sc::array_base const & x) { return x.stride()[0]; } int ld(sc::array_base const & x) { return x.stride()[1]; } template std::string join(Iterator begin, Iterator end, std::string delimiter){ std::string result; while(begin!=end){ result += *begin; if(++begin!=end) result += delimiter; } return result; } /*------ Simple Vector ---------*/ template class simple_vector_base { public: typedef T value_type; simple_vector_base(int_t start, int_t end, int_t stride, std::vector & data) : N_((end-start)/stride), start_(start), stride_(stride), data_(data) { } int_t size() const { return N_; } int_t start() const { return start_; } int_t stride() const { return stride_; } std::vector & data() { return data_; } T & operator[](int_t i) { return data_[start_ + i*stride_]; } T operator[](int_t i) const { return data_[start_ + i*stride_]; } private: int_t N_; int_t start_; int_t stride_; std::vector & data_; }; template class simple_vector : public simple_vector_base { public: simple_vector(int_t N) : simple_vector_base(0, N, 1, data_), data_(N){} private: std::vector data_; }; template class simple_vector_s : public simple_vector_base { public: simple_vector_s(simple_vector_base & x, int_t start, int_t end, int_t stride) : simple_vector_base(start, end, stride, x.data()){ } }; /*------ Simple Matrix ---------*/ template class simple_matrix_base { public: simple_matrix_base(int_t start1, int_t end1, int_t stride1, int_t start2, int_t end2, int_t stride2, int_t ld, std::vector & data) : M_((end1 - start1)/stride1), start1_(start1), stride1_(stride1), N_((end2-start2)/stride2), start2_(start2), stride2_(stride2), ld_(ld), data_(data) {} int_t size1() const { return M_; } int_t start1() const { return start1_; } int_t stride1() const { return stride1_; } int_t size2() const { return N_; } int_t start2() const { return start2_; } int_t stride2() const { return stride2_; } std::vector & data() { return data_; } int_t ld() const { return ld_; } T operator()(int_t i, int_t j) const { int_t ii = start1_ + i*stride1_; int_t jj = start2_ + j*stride2_; return data_[ii + jj*ld_]; } T& operator()(int_t i, int_t j) { int_t ii = start1_ + i*stride1_; int_t jj = start2_ + j*stride2_; return data_[ii + jj*ld_]; } private: int_t M_; int_t start1_; int_t stride1_; int_t N_; int_t start2_; int_t stride2_; int_t ld_; std::vector & data_; }; template class simple_matrix : public simple_matrix_base { public: simple_matrix(int_t M, int_t N) : simple_matrix_base(0, M, 1, 0, N, 1, M, data_), data_(M*N){} simple_matrix(int_t M, int_t N, std::vector data) : simple_matrix_base(0, M, 1, 0, N, 1, M, data_), data_(data){} private: std::vector data_; }; template class simple_matrix_s : public simple_matrix_base { public: simple_matrix_s(simple_matrix_base & A, int_t start1, int_t end1, int_t stride1, int_t start2, int_t end2, int_t stride2) : simple_matrix_base(start1, end1, stride1, start2, end2, stride2, A.ld(), A.data()){ } }; template std::ostream& operator<<(std::ostream& os, simple_matrix_base const & x) { for(size_t i = 0 ; i < (size_t)x.size1() ; i++){ for(size_t j = 0; j < (size_t)x.size2() ; j++) os << ((j>0)?",":"") << x(i,j); os << std::endl; } return os; } /*------ Initializer ---------*/ template std::vector random(size_t N) { std::vector res(N); for (size_t i = 0; i < res.size(); ++i) res[i] = (T)rand()/RAND_MAX; return res; } template void init_rand(simple_vector_base & x) { for (int_t i = 0; i < x.size(); ++i) x[i] = (T)rand()/RAND_MAX; } template void init_rand(simple_matrix_base & A) { for (int_t i = 0; i < A.size1(); ++i) for(int_t j = 0 ; j < A.size2() ; ++j) A(i,j) = (T)rand()/RAND_MAX; } template simple_matrix simple_trans(simple_matrix_base const & A) { simple_matrix res(A.size2(), A.size1()); for(int_t i = 0 ; i < A.size1(); ++i) for(int_t j = 0; j < A.size2(); ++j) res(j, i) = A(i, j); return res; } /*------ Compare -----------*/ template bool diff(T a, T b, T epsilon, typename std::enable_if::value>::type* = 0) { T delta = std::abs(a - b); if(std::max(a, b)!=0) delta/=std::abs(std::max(a, b)); return delta > epsilon; } template bool diff(VecType1 const & x, VecType2 const & y, typename VecType1::value_type epsilon) { assert((size_t)x.size()==(size_t)y.size()); typedef typename VecType1::value_type T; T max = 0; for(int_t i = 0 ; i < (int_t)x.size() ; ++i) { if(diff(x[i], y[i], epsilon)){ return true; } } return false; } template bool diff(VecType const & x, isaac::array const & iy, typename VecType::value_type epsilon) { VecType y(x.size()); isaac::copy(iy, y); return diff(x, y, epsilon); } template bool diff(isaac::array const & x, VecType const & y, typename VecType::value_type epsilon) { return diff(y, x, epsilon); } #define INIT_VECTOR(N, SUBN, START, STRIDE, CPREFIX, PREFIX, CTX) \ simple_vector CPREFIX(N);\ simple_vector_s CPREFIX ## _s(CPREFIX, START, START + STRIDE*SUBN, STRIDE);\ init_rand(CPREFIX);\ isaac::array PREFIX(CPREFIX.data(), CTX);\ isaac::view PREFIX ## _s = PREFIX[{START, START + STRIDE*SUBN, STRIDE}]; #define INIT_MATRIX(M, SUBM, START1, STRIDE1, N, SUBN, START2, STRIDE2, CPREFIX, PREFIX, CTX) \ simple_matrix CPREFIX(M, N);\ simple_matrix_s CPREFIX ## _s(CPREFIX, START1, START1 + STRIDE1*SUBM, STRIDE1,\ START2, START2 + STRIDE2*SUBN, STRIDE2);\ init_rand(CPREFIX);\ isaac::array PREFIX(M, N, CPREFIX.data(), CTX);\ isaac::view PREFIX ## _s(PREFIX({START1, START1 + STRIDE1*SUBM, STRIDE1},\ {START2, START2 + STRIDE2*SUBN, STRIDE2}));\ simple_matrix CPREFIX ## T = simple_trans(CPREFIX);\ isaac::array PREFIX ## T(N, M, CPREFIX ## T.data(), CTX);\ isaac::view PREFIX ## T_s(PREFIX ## T( {START2, START2 + STRIDE2*SUBN, STRIDE2},\ {START1, START1 + STRIDE1*SUBM, STRIDE1}));\ template int run_test(test_fun_t const & testf, test_fun_t const & testd) { int nfail = 0; int npass = 0; std::list data; sc::driver::backend::contexts::get(data); for(isaac::driver::Context const * context : data) { sc::driver::Device device = sc::driver::backend::queues::get(*context,0).device(); if(device.type() != sc::driver::Device::Type::GPU) continue; std::cout << "Device: " << device.name() << " on " << device.platform().name() << " " << device.platform().version() << std::endl; std::cout << "---" << std::endl; testf(*context, nfail, npass); if(device.fp64_support()) testd(*context, nfail, npass); std::cout << "---" << std::endl; } if(nfail>0) return EXIT_FAILURE; return EXIT_SUCCESS; } #define ADD_TEST_1D_EW(NAME, CPU_LOOP, GPU_EXPR) \ {\ simple_vector buffer(N);\ std::cout << NAME << "..." << std::flush;\ for(int_t i = 0 ; i < N ; ++i)\ CPU_LOOP;\ GPU_EXPR;\ isaac::copy(y, buffer.data());\ if(diff(cy, buffer, numeric_trait::epsilon)){\ nfail++;\ std::cout << " [FAIL] " << std::endl;\ }\ else{\ npass++;\ std::cout << std::endl;\ }\ } #define ADD_TEST_1D_RD(NAME, CPU_REDUCTION, INIT, ASSIGNMENT, GPU_REDUCTION) \ {\ T tmp = 0;\ std::cout << NAME << "..." << std::flush;\ cs = INIT;\ for(int_t i = 0 ; i < N ; ++i)\ CPU_REDUCTION;\ cs= ASSIGNMENT ;\ GPU_REDUCTION;\ tmp = ds;\ if(std::isnan((T)tmp) || (std::abs(cs - tmp)/std::max(cs, tmp)) > numeric_trait::epsilon){\ nfail++;\ std::cout << " [FAIL] " << std::endl;\ }\ else{\ npass++;\ std::cout << std::endl;\ }\ } #define ADD_TEST_2D_EW(NAME, CPU_LOOP, GPU_EXPR) \ {\ std::cout << NAME << "..." << std::flush;\ for(int_t i = 0 ; i < M ; ++i)\ for(int_t j = 0 ; j < N ; ++j)\ CPU_LOOP;\ GPU_EXPR;\ isaac::copy(C, buffer.data());\ std::vector cCbuffer(M*N);\ for(int i = 0 ; i < M ; ++i)\ for(int j = 0 ; j < N ; ++j)\ cCbuffer[i + j*M] = cC(i,j);\ if(diff(cCbuffer, buffer, numeric_trait::epsilon)) {\ nfail++;\ std::cout << " [Failure!]" << std::endl;\ }\ else{\ npass++;\ std::cout << std::endl;\ }\ } #define ADD_TEST_2D_RD(NAME, SIZE1, SIZE2, NEUTRAL, REDUCTION, ASSIGNMENT, GPU_REDUCTION, RES, BUF, CRES)\ {\ std::cout << NAME << "..." << std::flush;\ for(int i = 0 ; i < SIZE1 ; ++i)\ {\ yi = NEUTRAL;\ xi = NEUTRAL;\ for(int j = 0 ; j < SIZE2 ; ++j)\ REDUCTION;\ ASSIGNMENT;\ }\ GPU_REDUCTION;\ sc::copy(RES, BUF.data());\ if(diff(CRES, BUF, numeric_trait::epsilon)){\ nfail++;\ std::cout << " [FAIL] " << std::endl;\ }\ else{\ npass++;\ std::cout << std::endl;\ }\ } #define ADD_TEST_MATMUL(NAME, GPU_OP)\ {\ std::cout << NAME << "..." << std::flush;\ GPU_OP;\ sc::copy(C, buffer);\ if(diff(buffer, cCbuffer, numeric_trait::epsilon))\ {\ nfail++;\ std::cout << " [Failure!]" << std::endl;\ }\ else{\ npass++;\ std::cout << std::endl;\ }\ } #endif