tapasco_benchmark.cpp 6.28 KB
Newer Older
1
/**
2
 *  @file	tapasco_benchmark.cpp
3
4
5
6
7
8
9
10
11
12
13
14
15
 *  @brief	Benchmark application that generates a JSON file containing
 *              parameters for design space exploration. Also gives an overview
 *              of system performance.
 *  @author	J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
 **/
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <chrono>
#include <ctime>
#include <vector>
#include <sys/utsname.h>
16
#include <tapasco.hpp>
17
#include <platform.h>
18
#include <unistd.h>
19
20
21
#include "CumulativeAverage.hpp"
#include "TransferSpeed.hpp"
#include "InterruptLatency.hpp"
22
#include "JobThroughput.hpp"
23
24
25
#include "json11.hpp"

using namespace std;
26
using namespace tapasco;
27
28
using namespace json11;

29
30
31
32
33
34
typedef enum {
  MEASURE_TRANSFER_SPEED    = (1 << 0),
  MEASURE_INTERRUPT_LATENCY = (1 << 1),
  MEASURE_JOB_THROUGHPUT    = (1 << 2)
} measure_t;

35
36
37
38
39
40
41
42
43
44
45
46
47
struct transfer_speed_t {
  size_t chunk_sz;
  double speed_r;
  double speed_w;
  double speed_rw;
  Json to_json() const { return Json::object {
      {"Chunk Size", static_cast<int>(chunk_sz)},
      {"Read", speed_r},
      {"Write", speed_w},
      {"ReadWrite", speed_rw}
    }; }
};

Jens Korinth's avatar
Jens Korinth committed
48
49
50
struct interrupt_latency_t {
  size_t cycle_count;
  double latency_us;
51
52
  double min_latency_us;
  double max_latency_us;
Jens Korinth's avatar
Jens Korinth committed
53
54
  Json to_json() const { return Json::object {
      {"Cycle Count", static_cast<double>(cycle_count)},
55
56
57
      {"Avg Latency", latency_us},
      {"Min Latency", min_latency_us},
      {"Max Latency", max_latency_us}
Jens Korinth's avatar
Jens Korinth committed
58
59
    }; }
};
60

61
62
63
64
65
66
67
68
69
struct job_throughput_t {
  size_t num_threads;
  double jobs_per_sec;
  Json to_json() const { return Json::object {
      {"Number of threads", static_cast<double>(num_threads)},
      {"Jobs per second", jobs_per_sec}
    }; }
};

70
int main(int argc, const char *argv[]) {
71
72
73
74
75
76
77
78
79
80
81
82
83
  measure_t mode = static_cast<measure_t>(MEASURE_TRANSFER_SPEED | MEASURE_INTERRUPT_LATENCY | MEASURE_JOB_THROUGHPUT);
  if (argc > 1 && string(argv[0]).size()) {
    switch (argv[1][0]) {
    case 'm': mode = MEASURE_TRANSFER_SPEED; break;
    case 'i': mode = MEASURE_INTERRUPT_LATENCY; break;
    case 'j': mode = MEASURE_JOB_THROUGHPUT; break;
    case 'a': break;
    default:
      cerr << "Unknown mode: " << argv[0][0] << ". Choose one of a(ll), i(nterrupt latency), j(ob throughput), m(emory transfer speed)." << endl;
      exit(1);
    }
  }

84
  initscr(); noecho(); curs_set(1); timeout(0); raw();
85
86
87
88
89
90
91
92
93
94
95
96
97
  try {
    Tapasco tapasco;
    TransferSpeed tp { tapasco };
    InterruptLatency il { tapasco };
    JobThroughput jt { tapasco };
    struct utsname uts;
    uname(&uts);
    vector<Json> speed;
    struct transfer_speed_t ts;
    vector<Json> latency;
    struct interrupt_latency_t ls;
    vector<Json> jobs;
    struct job_throughput_t js;
98

99
    string platform = "vc709";
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    if (getenv("TAPASCO_PLATFORM") == NULL) {
      char n[256] { "" };
      cout << "Environment variable TAPASCO_PLATFORM is not set, guessing Platform ..." << endl;
      if (gethostname(n, 255))
        cerr << "Could not get host name, guessing vc709 Platform" << endl;
      else {
        cout << "Host name: " << n << endl;
        platform = n;
        if (string(n).compare("zed") == 0 || string(n).compare("zedboard") == 0)
          platform = "zedboard";
        if (string(n).compare("zc706") == 0)
          platform = "zc706";
        cout << "Guessing " << platform << " Platform" << endl;
      }
    } else platform = getenv("TAPASCO_PLATFORM");
115

116
    // measure for chunk sizes 2^10 (1KiB) - 2^31 (2GB) bytes
117
    for (int i = 10; mode & MEASURE_TRANSFER_SPEED && i < 32; ++i) {
118
119
120
121
      ts.chunk_sz = 1 << i;
      ts.speed_r  = tp(ts.chunk_sz, TransferSpeed::OP_COPYFROM);
      ts.speed_w  = tp(ts.chunk_sz, TransferSpeed::OP_COPYTO);
      ts.speed_rw = tp(ts.chunk_sz, TransferSpeed::OP_COPYFROM | TransferSpeed::OP_COPYTO);
Jens Korinth's avatar
Jens Korinth committed
122
      /*cout << "Transfer speed @ chunk_sz = " << (ts.chunk_sz/1024) << " KiB:"
123
124
125
           << " read "    << ts.speed_r  << " MiB/s"
           << ", write: " << ts.speed_w  << " MiB/s"
           << ", r/w: "   << ts.speed_rw << " MiB/s"
Jens Korinth's avatar
Jens Korinth committed
126
           << endl;*/
127
128
129
130
131
      if (ts.speed_r > 0.0 || ts.speed_w > 0 || ts.speed_rw > 0) {
        Json json = ts.to_json();
        speed.push_back(json);
      } else break;
    }
132

133
134
    // measure average job roundtrip latency for clock cycles counts
    // between 2^0 and 2^31
135
    for (size_t i = 0; mode & MEASURE_INTERRUPT_LATENCY && i < 32; ++i) {
136
137
      ls.cycle_count = 1UL << i;
      ls.latency_us  = il.atcycles(ls.cycle_count, 10, &ls.min_latency_us, &ls.max_latency_us);
Jens Korinth's avatar
Jens Korinth committed
138
      // cout << "Latency @ " << ls.cycle_count << "cc runtime: " << ls.latency_us << " us" << endl;
139
140
141
      Json json = ls.to_json();
      latency.push_back(json);
    }
142

143
144
145
146
    if (mode & MEASURE_JOB_THROUGHPUT) {
      size_t i = 1;
      double prev = -1;
      js.jobs_per_sec = -1;
147
      const size_t min_threads = sysconf(_SC_NPROCESSORS_ONLN) * 2;
148
149
150
151
152
153
      do {
        prev = js.jobs_per_sec;
        js.num_threads = i;
        js.jobs_per_sec = jt(i);
        ++i;
        jobs.push_back(js.to_json());
154
      } while (i <= 128 && (i <= min_threads || js.jobs_per_sec > prev));
155
    }
156

157
158
159
160
161
    // record current time
    time_t tt = chrono::system_clock::to_time_t(chrono::system_clock::now());
    tm tm = *localtime(&tt);
    stringstream str;
    str << put_time(&tm, "%Y-%m-%d %H:%M:%S");
162

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    // build JSON object
    Json benchmark = Json::object {
      {"Timestamp", str.str()},
      {"Host", Json::object {
          {"Operating System", uts.sysname},
          {"Node", uts.nodename},
          {"Release", uts.release},
          {"Version", uts.version},
          {"Machine", uts.machine}
        }
      },
      {"Transfer Speed", speed},
      {"Interrupt Latency", latency},
      {"Job Throughput", jobs},
      {"Library Versions", Json::object {
          {"Tapasco API",  tapasco::tapasco_version()},
          {"Platform API", platform::platform_version()}
        }
181
      }
182
    };
Jens Korinth's avatar
Jens Korinth committed
183
    endwin();
184

185
186
187
    // dump it
    stringstream ss;
    ss << platform << ".benchmark";
188
189
    cout << "Dumping benchmark Json to " << (argc >= 3 ? argv[2] : ss.str()) << endl;
    ofstream f(argc >= 3 ? argv[2] : ss.str());
190
191
    f << benchmark.dump();
    f.close();
192
193
194
  } catch (const char *msg) {
    endwin();
    cerr << "ERROR: " << msg << endl;
195
    exit(1);
196
197
198
199
  } catch (...) {
    endwin();
    throw;
  }
200
201
}
/* vim: set foldmarker=@{,@} foldlevel=0 foldmethod=marker : */