benchmark_runner.h 5.68 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12

#ifndef BENCHMARK_RUNNER_H
#define BENCHMARK_RUNNER_H

#include <string>
#include <cstdlib>
#include <vector>
#include <chrono>
#include <numeric>
#include <iostream>
#include <fstream>
#include <bits/stdc++.h>
FritzFlorian committed
13 14 15 16 17 18 19 20
#include <thread>
#include <map>

#include <tuple>
#include <unistd.h>
#include <string>
#include <fstream>
#include <sstream>
21 22 23 24 25 26 27 28 29 30 31

using namespace std;

class benchmark_runner {
 private:
  string csv_path_;
  string csv_name_;

  chrono::steady_clock::time_point last_start_time_;
  vector<long> times_;

32 33 34 35 36 37
  bool memory_stats_enabled_{false};
  const string MEMORY_PRE_RUN = "memory_pages_pre_run";
  const string MEMORY_POST_RUN = "memory_pages_post_run";
  unsigned long memory_pre_run_;
  unsigned long memory_post_run_;

FritzFlorian committed
38 39
  map<string, vector<long>> custom_stats_;

40 41 42
  void print_statistics() {
    long time_sum = std::accumulate(times_.begin(), times_.end(), 0l);
    cout << "Average Runtime (us): " << (time_sum / times_.size()) << endl;
FritzFlorian committed
43 44 45 46 47

    for (auto &iter : custom_stats_) {
      long custom_stat_sum = std::accumulate(iter.second.begin(), iter.second.end(), 0l);
      cout << "Average " << iter.first << ": " << (custom_stat_sum / times_.size()) << endl;
    }
48 49 50 51 52 53 54 55
  }

  inline bool file_exists(const std::string &name) {
    ifstream f(name);
    return f.good();
  }

 public:
FritzFlorian committed
56 57 58
  benchmark_runner(string csv_path, string csv_name) : csv_path_{std::move(csv_path)},
                                                       csv_name_{std::move(csv_name)},
                                                       times_{} {
59 60 61 62 63 64
    string command = "mkdir -p " + csv_path_;
    int res = system(command.c_str());
    if (res) {
      cout << "Error while creating directory!" << endl;
      exit(1);
    }
FritzFlorian committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
  }

  /**
   * Queries the pages used by the current process.
   * Splits between private and shared pages.
   *
   * @return Tuple(private_pages, shared_pages) used by the process.
   */
  static std::pair<unsigned long, unsigned long> query_process_memory_pages() {
    pid_t my_pid = getpid();
    std::ostringstream proc_stats_path_builder;
    proc_stats_path_builder << "/proc/" << my_pid << "/smaps_rollup";
    std::string proc_stats_path = proc_stats_path_builder.str();

    std::ifstream proc_stats_file{proc_stats_path};

    std::string line;
    unsigned long total_shared = 0;
    unsigned long total_private = 0;
    while (std::getline(proc_stats_file, line)) {
      std::stringstream line_input(line);
      std::string type;
      unsigned long size;
      line_input >> type >> size;

      if (type.find("Shared") != std::string::npos) {
        total_shared += size;
      }
      if (type.find("Private") != std::string::npos) {
        total_private += size;
      }
    }

    return std::make_pair(total_private, total_shared);
  }
100

FritzFlorian committed
101 102
  void add_custom_stats_field(const string name) {
    custom_stats_.insert({name, {}});
103 104
  }

105 106 107 108 109 110
  void enable_memory_stats() {
    memory_stats_enabled_ = true;
    add_custom_stats_field(MEMORY_PRE_RUN);
    add_custom_stats_field(MEMORY_POST_RUN);
  }

111 112 113 114 115 116 117 118 119 120 121 122 123
  static void read_args(int argc, char **argv, int &num_threads, string &path) {
    if (argc < 3) {
      cout << "Must Specifiy concurrency and output directory! (usage: `benchmark <output_directory> <num_threads>`)"
           << endl;
      exit(1);
    }

    string tmp = argv[1];
    path = tmp;
    num_threads = atoi(argv[2]);
  }

  void start_iteration() {
124 125 126 127 128
    if (memory_stats_enabled_) {
      auto memory_stats = query_process_memory_pages();
      memory_pre_run_ = memory_stats.first;
    }

129 130 131 132
    last_start_time_ = chrono::steady_clock::now();
  }

  void end_iteration() {
133 134
    size_t iteration_index = times_.size();

135 136
    auto end_time = chrono::steady_clock::now();
    long time = chrono::duration_cast<chrono::microseconds>(end_time - last_start_time_).count();
FritzFlorian committed
137

138
    times_.emplace_back(time);
FritzFlorian committed
139 140 141
    for (auto &iter : custom_stats_) {
      iter.second.emplace_back(0);
    }
142 143 144 145 146 147 148
    if (memory_stats_enabled_) {
      auto memory_stats = query_process_memory_pages();
      memory_post_run_ = memory_stats.first;

      custom_stats_[MEMORY_PRE_RUN][iteration_index] = memory_pre_run_;
      custom_stats_[MEMORY_POST_RUN][iteration_index] = memory_post_run_;
    }
FritzFlorian committed
149 150 151 152 153
  }

  void store_custom_stat(const string &name, long value) {
    auto &stat_vector = custom_stats_[name];
    stat_vector[stat_vector.size() - 1] = value;
154 155
  }

156 157 158
  void run_iterations(int count,
                      const function<void(void)> measure,
                      int warmup_count,
FritzFlorian committed
159 160
                      const function<void(void)> prepare = []() {},
                      const function<void(void)> finish = []() {}) {
161
    for (int i = 0; i < warmup_count; i++) {
162 163
      prepare();
      measure();
164 165 166
    }

    for (int i = 0; i < count; i++) {
FritzFlorian committed
167 168
      using namespace std::literals;
      this_thread::sleep_for(100us);
169
      prepare();
170
      start_iteration();
171
      measure();
172
      end_iteration();
FritzFlorian committed
173
      finish();
174 175 176 177 178 179 180 181 182 183 184 185 186
    }
  }

  void commit_results(bool print_stats) {
    if (print_stats) {
      print_statistics();
    }

    string full_filename = csv_path_ + csv_name_;
    bool write_header = !file_exists(full_filename);

    { // Scope for output file
      ofstream o(full_filename, std::fstream::out | std::fstream::app);
FritzFlorian committed
187

188
      if (write_header) {
FritzFlorian committed
189 190 191 192 193
        o << "runtime_us";
        for (auto &iter : custom_stats_) {
          o << ";" << iter.first;
        }
        o << endl;
194
      }
FritzFlorian committed
195 196 197 198 199 200 201 202

      // TODO: make this more efficient
      for (size_t i = 0; i < times_.size(); i++) {
        o << times_[i];
        for (auto &iter : custom_stats_) {
          o << ";" << iter.second[i];
        }
        o << endl;
203 204 205 206
      }
    } // End Scope for output file

    times_.clear();
FritzFlorian committed
207 208 209
    for (auto &iter : custom_stats_) {
      iter.second.clear();
    }
210 211 212 213
  }
};

#endif //BENCHMARK_RUNNER_H