benchmark_runner.h 4.85 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_;

FritzFlorian committed
32 33
  map<string, vector<long>> custom_stats_;

34 35 36
  void print_statistics() {
    long time_sum = std::accumulate(times_.begin(), times_.end(), 0l);
    cout << "Average Runtime (us): " << (time_sum / times_.size()) << endl;
FritzFlorian committed
37 38 39 40 41

    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;
    }
42 43 44 45 46 47 48 49
  }

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

 public:
FritzFlorian committed
50 51 52
  benchmark_runner(string csv_path, string csv_name) : csv_path_{std::move(csv_path)},
                                                       csv_name_{std::move(csv_name)},
                                                       times_{} {
53 54 55 56 57 58
    string command = "mkdir -p " + csv_path_;
    int res = system(command.c_str());
    if (res) {
      cout << "Error while creating directory!" << endl;
      exit(1);
    }
FritzFlorian committed
59 60 61 62 63 64 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
  }

  /**
   * 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);
  }
94

FritzFlorian committed
95 96
  void add_custom_stats_field(const string name) {
    custom_stats_.insert({name, {}});
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
  }

  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() {
    last_start_time_ = chrono::steady_clock::now();
  }

  void end_iteration() {
    auto end_time = chrono::steady_clock::now();
    long time = chrono::duration_cast<chrono::microseconds>(end_time - last_start_time_).count();
    times_.emplace_back(time);
FritzFlorian committed
119 120 121 122 123 124 125 126 127

    for (auto &iter : custom_stats_) {
      iter.second.emplace_back(0);
    }
  }

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

130 131 132
  void run_iterations(int count,
                      const function<void(void)> measure,
                      int warmup_count,
FritzFlorian committed
133 134
                      const function<void(void)> prepare = []() {},
                      const function<void(void)> finish = []() {}) {
135
    for (int i = 0; i < warmup_count; i++) {
136 137
      prepare();
      measure();
138 139 140
    }

    for (int i = 0; i < count; i++) {
FritzFlorian committed
141 142
      using namespace std::literals;
      this_thread::sleep_for(100us);
143
      prepare();
144
      start_iteration();
145
      measure();
146
      end_iteration();
FritzFlorian committed
147
      finish();
148 149 150 151 152 153 154 155 156 157 158 159 160
    }
  }

  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
161

162
      if (write_header) {
FritzFlorian committed
163 164 165 166 167
        o << "runtime_us";
        for (auto &iter : custom_stats_) {
          o << ";" << iter.first;
        }
        o << endl;
168
      }
FritzFlorian committed
169 170 171 172 173 174 175 176

      // 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;
177 178 179 180
      }
    } // End Scope for output file

    times_.clear();
FritzFlorian committed
181 182 183
    for (auto &iter : custom_stats_) {
      iter.second.clear();
    }
184 185 186 187
  }
};

#endif //BENCHMARK_RUNNER_H