#include "dnn.hpp" #include void run_taskflow(MNIST& D, unsigned num_threads) { tf::Executor executor(num_threads); tf::Taskflow taskflow; std::vector forward_tasks; std::vector backward_tasks; std::vector update_tasks; std::vector shuffle_tasks; // Number of parallel shuffle const auto num_storage = num_threads; const auto num_par_shf = std::min(num_storage, D.epoch); std::vector mats(num_par_shf, D.images); std::vector vecs(num_par_shf, D.labels); // Create task flow graph const auto iter_num = D.images.rows()/D.batch_size; for(auto e=0u; e=0; j--) { // backward propagation backward_tasks.emplace_back(taskflow.emplace( [&, i=j, e=e%num_par_shf] () { backward_task(D, i, e, mats); } )); auto& b_task = backward_tasks.back(); // update weight update_tasks.emplace_back( taskflow.emplace([&, i=j] () {D.update(i);}) ); auto& u_task = update_tasks.back(); if(j + 1u == D.acts.size()) { f_task.precede(b_task); } else { backward_tasks[backward_tasks.size()-2].precede(b_task); } b_task.precede(u_task); } // End of backward propagation } // End of all iterations (task flow graph creation) if(e == 0) { // No need to shuffle in first epoch shuffle_tasks.emplace_back(taskflow.emplace([](){})); shuffle_tasks.back().precede(forward_tasks[forward_tasks.size()-iter_num]); } else { shuffle_tasks.emplace_back(taskflow.emplace( [&, e=e%num_par_shf]() { D.shuffle(mats[e], vecs[e], D.images.rows());} )); auto& t = shuffle_tasks.back(); t.precede(forward_tasks[forward_tasks.size()-iter_num]); // This shuffle task starts after belows finish // 1. previous shuffle on the same storage // 2. the last backward task of previous epoch on the same storage if(e >= num_par_shf) { auto prev_e = e - num_par_shf; shuffle_tasks[prev_e].precede(t); int task_id = (prev_e+1)*iter_num*D.acts.size() - 1; backward_tasks[task_id].precede(t); } } } // End of all epoch executor.run(taskflow).get(); }