pipeline.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. #ifndef OPENCV_GAPI_PIPELINE_MODELING_TOOL_PIPELINE_HPP
  2. #define OPENCV_GAPI_PIPELINE_MODELING_TOOL_PIPELINE_HPP
  3. #include <iomanip>
  4. struct PerfReport {
  5. std::string name;
  6. double avg_latency = 0.0;
  7. double min_latency = 0.0;
  8. double max_latency = 0.0;
  9. double first_latency = 0.0;
  10. double throughput = 0.0;
  11. double elapsed = 0.0;
  12. double warmup_time = 0.0;
  13. int64_t num_late_frames = 0;
  14. std::vector<double> latencies;
  15. std::vector<int64_t> seq_ids;
  16. std::string toStr(bool expanded = false) const;
  17. };
  18. std::string PerfReport::toStr(bool expand) const {
  19. const auto to_double_str = [](double val) {
  20. std::stringstream ss;
  21. ss << std::fixed << std::setprecision(3) << val;
  22. return ss.str();
  23. };
  24. std::stringstream ss;
  25. ss << name << ": warm-up: " << to_double_str(warmup_time)
  26. << " ms, execution time: " << to_double_str(elapsed)
  27. << " ms, throughput: " << to_double_str(throughput)
  28. << " FPS, latency: first: " << to_double_str(first_latency)
  29. << " ms, min: " << to_double_str(min_latency)
  30. << " ms, avg: " << to_double_str(avg_latency)
  31. << " ms, max: " << to_double_str(max_latency)
  32. << " ms, frames: " << num_late_frames << "/" << seq_ids.back()+1 << " (dropped/all)";
  33. if (expand) {
  34. for (size_t i = 0; i < latencies.size(); ++i) {
  35. ss << "\nFrame:" << i << "\nLatency: "
  36. << to_double_str(latencies[i]) << " ms";
  37. }
  38. }
  39. return ss.str();
  40. }
  41. class StopCriterion {
  42. public:
  43. using Ptr = std::unique_ptr<StopCriterion>;
  44. virtual void start() = 0;
  45. virtual void iter() = 0;
  46. virtual bool done() = 0;
  47. virtual ~StopCriterion() = default;
  48. };
  49. class Pipeline {
  50. public:
  51. using Ptr = std::shared_ptr<Pipeline>;
  52. Pipeline(std::string&& name,
  53. cv::GComputation&& comp,
  54. std::shared_ptr<DummySource>&& src,
  55. StopCriterion::Ptr stop_criterion,
  56. cv::GCompileArgs&& args,
  57. const size_t num_outputs);
  58. void compile();
  59. void run();
  60. const PerfReport& report() const;
  61. const std::string& name() const { return m_name;}
  62. virtual ~Pipeline() = default;
  63. protected:
  64. virtual void _compile() = 0;
  65. virtual void run_iter() = 0;
  66. virtual void init() {};
  67. virtual void deinit() {};
  68. void prepareOutputs();
  69. std::string m_name;
  70. cv::GComputation m_comp;
  71. std::shared_ptr<DummySource> m_src;
  72. StopCriterion::Ptr m_stop_criterion;
  73. cv::GCompileArgs m_args;
  74. size_t m_num_outputs;
  75. PerfReport m_perf;
  76. cv::GRunArgsP m_pipeline_outputs;
  77. std::vector<cv::Mat> m_out_mats;
  78. int64_t m_start_ts;
  79. int64_t m_seq_id;
  80. };
  81. Pipeline::Pipeline(std::string&& name,
  82. cv::GComputation&& comp,
  83. std::shared_ptr<DummySource>&& src,
  84. StopCriterion::Ptr stop_criterion,
  85. cv::GCompileArgs&& args,
  86. const size_t num_outputs)
  87. : m_name(std::move(name)),
  88. m_comp(std::move(comp)),
  89. m_src(std::move(src)),
  90. m_stop_criterion(std::move(stop_criterion)),
  91. m_args(std::move(args)),
  92. m_num_outputs(num_outputs) {
  93. m_perf.name = m_name;
  94. }
  95. void Pipeline::compile() {
  96. m_perf.warmup_time =
  97. utils::measure<utils::double_ms_t>([this]() {
  98. _compile();
  99. });
  100. }
  101. void Pipeline::prepareOutputs() {
  102. // NB: N-2 buffers + timestamp + seq_id.
  103. m_out_mats.resize(m_num_outputs - 2);
  104. for (auto& m : m_out_mats) {
  105. m_pipeline_outputs += cv::gout(m);
  106. }
  107. m_pipeline_outputs += cv::gout(m_start_ts);
  108. m_pipeline_outputs += cv::gout(m_seq_id);
  109. }
  110. void Pipeline::run() {
  111. using namespace std::chrono;
  112. // NB: Allocate outputs for execution
  113. prepareOutputs();
  114. // NB: Warm-up iteration invalidates source state
  115. // so need to copy it
  116. auto orig_src = m_src;
  117. auto copy_src = std::make_shared<DummySource>(*m_src);
  118. // NB: Use copy for warm-up iteration
  119. m_src = copy_src;
  120. // NB: Warm-up iteration
  121. init();
  122. run_iter();
  123. deinit();
  124. // NB: Calculate first latency
  125. m_perf.first_latency = utils::double_ms_t{
  126. microseconds{utils::timestamp<microseconds>() - m_start_ts}}.count();
  127. // NB: Now use original source
  128. m_src = orig_src;
  129. // NB: Start measuring execution
  130. init();
  131. auto start = high_resolution_clock::now();
  132. m_stop_criterion->start();
  133. while (true) {
  134. run_iter();
  135. const auto latency = utils::double_ms_t{
  136. microseconds{utils::timestamp<microseconds>() - m_start_ts}}.count();
  137. m_perf.latencies.push_back(latency);
  138. m_perf.seq_ids.push_back(m_seq_id);
  139. m_stop_criterion->iter();
  140. if (m_stop_criterion->done()) {
  141. m_perf.elapsed = duration_cast<utils::double_ms_t>(
  142. high_resolution_clock::now() - start).count();
  143. deinit();
  144. break;
  145. }
  146. }
  147. m_perf.avg_latency = utils::avg(m_perf.latencies);
  148. m_perf.min_latency = utils::min(m_perf.latencies);
  149. m_perf.max_latency = utils::max(m_perf.latencies);
  150. // NB: Count the number of dropped frames
  151. int64_t prev_seq_id = m_perf.seq_ids[0];
  152. for (size_t i = 1; i < m_perf.seq_ids.size(); ++i) {
  153. m_perf.num_late_frames += m_perf.seq_ids[i] - prev_seq_id - 1;
  154. prev_seq_id = m_perf.seq_ids[i];
  155. }
  156. m_perf.throughput = (m_perf.latencies.size() / m_perf.elapsed) * 1000;
  157. }
  158. const PerfReport& Pipeline::report() const {
  159. return m_perf;
  160. }
  161. class StreamingPipeline : public Pipeline {
  162. public:
  163. using Pipeline::Pipeline;
  164. private:
  165. void _compile() override {
  166. m_compiled =
  167. m_comp.compileStreaming({m_src->descr_of()},
  168. cv::GCompileArgs(m_args));
  169. }
  170. virtual void init() override {
  171. m_compiled.setSource(m_src);
  172. m_compiled.start();
  173. }
  174. virtual void deinit() override {
  175. m_compiled.stop();
  176. }
  177. virtual void run_iter() override {
  178. m_compiled.pull(cv::GRunArgsP{m_pipeline_outputs});
  179. }
  180. cv::GStreamingCompiled m_compiled;
  181. };
  182. class RegularPipeline : public Pipeline {
  183. public:
  184. using Pipeline::Pipeline;
  185. private:
  186. void _compile() override {
  187. m_compiled =
  188. m_comp.compile({m_src->descr_of()},
  189. cv::GCompileArgs(m_args));
  190. }
  191. virtual void run_iter() override {
  192. cv::gapi::wip::Data data;
  193. m_src->pull(data);
  194. m_compiled({data}, cv::GRunArgsP{m_pipeline_outputs});
  195. }
  196. cv::GCompiled m_compiled;
  197. };
  198. enum class PLMode {
  199. REGULAR,
  200. STREAMING
  201. };
  202. #endif // OPENCV_GAPI_PIPELINE_MODELING_TOOL_PIPELINE_HPP