// // MIT License // Copyright (c) 2019 Jonathan R. Madsen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // --------------------------------------------------------------- // // /// \file gpu_tasking.cc /// \brief Example showing the usage of tasking with GPU // #include "common/utils.hh" #include "sum.hh" //============================================================================// ThreadPool*& GetGpuPool() { static thread_local ThreadPool* _instance = nullptr; return _instance; } //============================================================================// TaskManager*& GetGpuManager() { static thread_local TaskManager* _instance = nullptr; return _instance; } //============================================================================// void execute_cpu_iterations(uint64_t num_iter, TaskGroup_t* task_group, uint64_t n, uint64_t& remaining) { if(remaining <= 0 || !task_group) return; if(num_iter > remaining) num_iter = remaining; remaining -= num_iter; // add an element of randomness static std::atomic_uintmax_t _counter; uintmax_t _seed = get_seed() + (++_counter * 10000); get_engine().seed(_seed); std::stringstream ss; ss << cprefix << "Submitting " << num_iter << " tasks computing \"fibonacci(" << n << ")\" to task manager " << "(" << remaining << " iterations remaining)..." << std::flush; TaskManager* taskManager = TaskRunManager::GetMasterRunManager()->GetTaskManager(); Timer t; t.Start(); for(uint32_t i = 0; i < num_iter; ++i) { int offset = get_random_int(); taskManager->exec(*task_group, fibonacci, n + offset); } t.Stop(); ss << " " << t << endl; AutoLock l(TypeMutex()); cout << ss.str(); } //============================================================================// void execute_gpu_iterations(uint64_t num_iter, TaskGroup_t* task_group, uint64_t n) { if(!task_group) return; // add an element of randomness static std::atomic_uintmax_t _counter; uintmax_t _seed = get_seed() + (++_counter * 10000); get_engine().seed(_seed); TaskManager* taskManager = GetGpuManager(); std::stringstream ss; ss << cprefix << "Submitting " << num_iter << " tasks computing \"run_gpu(" << n << ")\" to task manager " << "..." << std::flush; Timer t; t.Start(); for(uint32_t i = 0; i < num_iter; ++i) { int offset_a = get_random_int(); int offset_b = get_random_int(); taskManager->exec(*task_group, run_gpu, n + (offset_a * offset_b)); } t.Stop(); ss << " " << t << endl; AutoLock l(TypeMutex()); cout << ss.str(); } //============================================================================// int main() { _pause_collection; // for VTune auto hwthreads = std::thread::hardware_concurrency(); auto default_fib = 42; auto default_tg = 2; auto default_gpu = 52; auto default_nthreads = (useTBB) ? (2 * hwthreads) : hwthreads; // default environment controls but don't overwrite setenv("NUM_THREADS", std::to_string(default_nthreads).c_str(), 0); setenv("FIBONACCI", std::to_string(default_fib).c_str(), 0); setenv("GPU_RANGE", std::to_string(default_gpu).c_str(), 0); setenv("GRAINSIZE", std::to_string(hwthreads).c_str(), 0); setenv("NUM_TASKS", std::to_string(hwthreads * hwthreads).c_str(), 0); setenv("NUM_TASK_GROUPS", std::to_string(default_tg).c_str(), 0); rng_range = GetEnv("RNG_RANGE", rng_range, "Setting RNG range to +/- this value"); unsigned numThreads = GetEnv("NUM_THREADS", default_nthreads, "Getting the number of threads"); uint64_t nfib = GetEnv("FIBONACCI", default_fib, "Setting the centerpoint of fib work distribution"); uint64_t ngpu = GetEnv("GPU_RANGE", default_gpu, "Setting the GPU range centerpoint"); uint64_t grainsize = GetEnv( "GRAINSIZE", numThreads, "Dividing number of task into grain of this size"); uint64_t num_iter = GetEnv("NUM_TASKS", numThreads * numThreads, "Setting the number of total tasks"); uint64_t num_groups = GetEnv("NUM_TASK_GROUPS", 4, "Setting the number of task groups"); PrintEnv(); Timer total_timer; total_timer.Start(); // Construct the default run manager TaskRunManager* runManager = new TaskRunManager(useTBB); // TaskRunManager* runManager = TaskRunManager::GetMasterRunManager(useTBB); runManager->Initialize(numThreads); message(runManager); // the TaskManager is a utility that wraps the // function calls into tasks for the ThreadPool TaskManager* taskManager = runManager->GetTaskManager(); ThreadPool*& gpu_tp = GetGpuPool(); TaskManager*& gpu_man = GetGpuManager(); if(!useTBB) { gpu_tp = new ThreadPool(numThreads * numThreads, nullptr, false); gpu_man = new TaskManager(gpu_tp); } else { gpu_tp = runManager->GetThreadPool(); gpu_man = taskManager; } //------------------------------------------------------------------------// // // // Asynchronous examples/tests // // // //------------------------------------------------------------------------// { auto fib_async = taskManager->async(fibonacci, nfib); uint64_t fib_n = fib_async->get(); std::cout << prefix << "[async test] fibonacci(" << nfib << " +/- " << rng_range << ") = " << fib_n << std::endl; std::cout << std::endl; } //------------------------------------------------------------------------// // // // Task-group example/test // // // //------------------------------------------------------------------------// std::atomic_uintmax_t true_answer(0); ///======================================================================/// /// /// /// /// /// PRIMARY TASKING SECTION /// /// /// /// /// ///======================================================================/// // this function joins task results auto cpu_join = [&](Array_t& ref, const uint64_t& thread_local_solution) { true_answer += thread_local_solution; ref.push_back(thread_local_solution); return ref; }; //------------------------------------------------------------------------// // this function joins task results auto gpu_join = [](Array_t& ref, const uint64_t& thread_local_solution) { ref.push_back(thread_local_solution); return ref; }; //------------------------------------------------------------------------// // this function deletes task groups auto del = [](TaskGroup_t*& _task_group) { delete _task_group; _task_group = nullptr; }; //------------------------------------------------------------------------// // create a task group auto cpu_create = [=](TaskGroup_t*& _task_group) { if(!_task_group) _task_group = new TaskGroup_t(cpu_join); }; //------------------------------------------------------------------------// // create a task group auto gpu_create = [=](TaskGroup_t*& _task_group) { if(!_task_group) _task_group = new TaskGroup_t(gpu_join, gpu_tp); }; //------------------------------------------------------------------------// std::vector cpu_task_groups(num_groups, nullptr); std::vector gpu_task_groups(num_groups, nullptr); uint64_t remaining = num_iter; ///======================================================================/// /// /// /// /// /// FAKE SECTION /// /// /// /// /// ///======================================================================/// std::cout << cprefix << "BEGIN OF FAKE RUN" << std::endl; //------------------------------------------------------------------------// for(uint64_t i = 0; i < cpu_task_groups.size(); ++i) { // create the task group cpu_create(cpu_task_groups[i]); gpu_create(gpu_task_groups[i]); // submit task with first task group execute_cpu_iterations(hwthreads, cpu_task_groups[i], hwthreads, remaining); execute_gpu_iterations(hwthreads, gpu_task_groups[i], hwthreads); } //------------------------------------------------------------------------// // make sure all task groups finished (does join) for(uint64_t i = 0; i < cpu_task_groups.size(); ++i) { cpu_task_groups[i]->join(); del(cpu_task_groups[i]); gpu_task_groups[i]->join(); del(gpu_task_groups[i]); } //------------------------------------------------------------------------// std::cout << cprefix << "END OF FAKE RUN\n" << std::endl; //------------------------------------------------------------------------// ///======================================================================/// /// /// /// /// /// WORK SECTION /// /// /// /// /// ///======================================================================/// Timer timer; //------------------------------------------------------------------------// timer.Start(); // start timer for calculation _resume_collection; // for VTune //------------------------------------------------------------------------// //------------------------------------------------------------------------// std::vector cpu_results(num_groups); std::vector gpu_results(num_groups); remaining = num_iter; true_answer.store(0); //------------------------------------------------------------------------// while(remaining > 0) { for(uint64_t i = 0; i < cpu_task_groups.size(); ++i) { // create the task group cpu_create(cpu_task_groups[i]); gpu_create(gpu_task_groups[i]); // submit task with first task group execute_cpu_iterations(grainsize, cpu_task_groups[i], nfib, remaining); execute_gpu_iterations(grainsize, gpu_task_groups[i], ngpu); } } ///======================================================================/// /// /// /// /// /// JOIN RESULTS /// /// /// /// /// ///======================================================================/// // make sure all task groups finished (does join) for(uint64_t i = 0; i < cpu_task_groups.size(); ++i) append(cpu_results[i], cpu_task_groups[i]); Timer diff; diff.Start(); std::cout << prefix << "CPU completed" << std::endl; for(uint64_t i = 0; i < gpu_task_groups.size(); ++i) append(gpu_results[i], gpu_task_groups[i]); diff.Stop(); std::cout << cprefix << "GPU completed" << std::endl; std::cout << cprefix << "CPU vs. GPU imbalance:\t\t" << diff << std::endl; // compute the anser uint64_t cpu_answer = 0; uint64_t gpu_answer = 0; for(uint64_t i = 0; i < cpu_task_groups.size(); ++i) { cpu_answer += compute_sum(cpu_results[i]); gpu_answer += compute_sum(gpu_results[i]); } //------------------------------------------------------------------------// _pause_collection; // for VTune timer.Stop(); // stop timer for fibonacci //------------------------------------------------------------------------// ///======================================================================/// /// /// /// /// /// END OF PRIMARY TASKING SECTION /// /// /// /// /// ///======================================================================/// std::cout << prefix << "[task group] fibonacci(" << nfib << " +/- " << rng_range << ") * " << num_iter << " = " << cpu_answer << std::endl; std::cout << cprefix << "[task group] run_gpu(" << ngpu << " +/- " << rng_range << ") * " << num_iter << " = " << gpu_answer << std::endl; std::cout << cprefix << "[atomic] gpu-tasking answer: " << true_answer << std::endl; std::stringstream fibprefix; fibprefix << "gpu-tasking(...) calculation time: "; int32_t _w = fibprefix.str().length() + 2; std::cout << prefix << std::setw(_w) << fibprefix.str() << "\t" << timer << std::endl; // KNL hangs somewhere between finishing calculations and total_timer Timer del_timer; del_timer.Start(); for(uint64_t i = 0; i < cpu_task_groups.size(); ++i) del(cpu_task_groups[i]); for(uint64_t i = 0; i < gpu_task_groups.size(); ++i) del(gpu_task_groups[i]); del_timer.Stop(); std::cout << cprefix << std::setw(_w) << "Task group deletion time: " << "\t" << del_timer << std::endl; // print the time for the calculation total_timer.Stop(); std::cout << cprefix << std::setw(_w) << "Total time: " << "\t" << total_timer << std::endl; uintmax_t ret = (true_answer - cpu_answer); if(ret == 0 && num_iter == gpu_answer) std::cout << prefix << "Successful MT gpu-tasking calculation" << std::endl; else std::cout << prefix << "Failure combining MT gpu-tasking calculation " << std::endl; std::cout << std::endl; delete runManager; return static_cast(ret); } //============================================================================//