// This program computes the dot products over a set of independent vectors // and compares the runtime between baseline (sequential), OpenMP, C++ thread, // and Taskflow implementations. #include #include #include #include using matrix_t = std::vector>; // ---------------------------------------------------------------------------- // Utility section // ---------------------------------------------------------------------------- // Function: random_matrix matrix_t random_matrix(size_t N) { thread_local std::default_random_engine gen(0); std::normal_distribution d{0.0f, 1.0f}; std::ostringstream oss; oss << "|----> generating " << N << "x" << N << " matrix by thread " << std::this_thread::get_id() << "\n"; std::cout << oss.str(); matrix_t mat(N); for(auto& r : mat) { r.resize(N); for(auto& c : r) { c = d(gen); } } return mat; } // Operator: multiplication matrix_t operator * (const matrix_t& A, const matrix_t& B) { if(A.empty() || B.empty() || A[0].size() != B.size()) { std::cout << A[0].size() << " " << B.size() << std::endl; throw std::runtime_error("Dimension mismatched in matrix multiplication\n"); } size_t M, K, N; N = A.size(); K = A[0].size(); M = B[0].size(); printf("A[%zux%zu] * B[%zux%zu]\n", N, K, K, M); // Initialize the matrix matrix_t ret(N); for(auto& r : ret) { r.resize(M); for(auto& c : r) { c = 0.0f; } } // Matrix multiplication for(size_t i=0; i& D) { std::cout << "========== baseline ==========\n"; auto tbeg = std::chrono::steady_clock::now(); std::cout << "Generating matrix As ...\n"; std::vector As(D.size()); for(size_t j=0; j Bs(D.size()); for(size_t j=0; j Cs(D.size()); for(size_t j=0; j(tend-tbeg).count() << " ms\n"; } // Procedure: openmp void openmp(const std::vector& D) { std::cout << "========== OpenMP ==========\n"; auto tbeg = std::chrono::steady_clock::now(); std::cout << "Generating matrix As ...\n"; std::vector As(D.size()); #pragma omp parallel for for(int j=0; j<(int)D.size(); ++j) { As[j] = random_matrix(D[j]); } std::cout << "Generating matrix Bs ...\n"; std::vector Bs(D.size()); #pragma omp parallel for for(int j=0; j<(int)D.size(); ++j) { Bs[j] = random_matrix(D[j]); } std::cout << "Computing matrix product values Cs ...\n"; std::vector Cs(D.size()); #pragma omp parallel for for(int j=0; j<(int)D.size(); ++j) { Cs[j] = As[j] * Bs[j]; } auto tend = std::chrono::steady_clock::now(); std::cout << "OpenMP takes " << std::chrono::duration_cast(tend-tbeg).count() << " ms\n"; } // Procedure: cppthread void cppthread(const std::vector& D) { std::cout << "========== CppThread ==========\n"; auto tbeg = std::chrono::steady_clock::now(); tf::SimpleThreadpool tpl(std::thread::hardware_concurrency()); std::cout << "Generating matrix As ...\n"; std::vector As(D.size()); std::vector> futures; for(size_t j=0; j Bs(D.size()); for(size_t j=0; j Cs(D.size()); for(size_t j=0; j(tend-tbeg).count() << " ms\n"; } // Procedure: taskflow void taskflow(const std::vector& D) { auto tbeg = std::chrono::steady_clock::now(); tf::Taskflow tf; std::cout << "Generating task As ...\n"; std::vector As(D.size()); std::vector TaskAs; for(size_t j=0; j Bs(D.size()); std::vector TaskBs; for(size_t j=0; j Cs(D.size()); std::vector TaskCs; for(size_t j=0; j(tend-tbeg).count() << " ms\n"; } // ------------------------------------------------------------------------------------------------ // Function: main int main(int argc, char* argv[]) { if(argc != 3) { std::cerr << "usage: ./matrix [baseline|openmp|cppthread|taskflow] N\n"; std::exit(EXIT_FAILURE); } // Create a unbalanced dimension for vector products. const auto N = std::stoul(argv[2]); std::vector dimensions(N); std::default_random_engine engine(0); std::uniform_int_distribution dis(1, 1000); std::cout << "matrix sizes = ["; for(size_t i=0; i