X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fdmclock%2Fsim%2Fsrc%2Fsimulate.h;fp=src%2Fceph%2Fsrc%2Fdmclock%2Fsim%2Fsrc%2Fsimulate.h;h=a967d004b6dcfcb0658edc2aa7740a6e8ffb5a21;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/dmclock/sim/src/simulate.h b/src/ceph/src/dmclock/sim/src/simulate.h new file mode 100644 index 0000000..a967d00 --- /dev/null +++ b/src/ceph/src/dmclock/sim/src/simulate.h @@ -0,0 +1,441 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +/* + * Copyright (C) 2016 Red Hat Inc. + */ + + +#pragma once + + +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace crimson { + namespace qos_simulation { + + template + class Simulation { + + public: + + using TimePoint = std::chrono::time_point; + + protected: + + using ClientMap = std::map; + using ServerMap = std::map; + + uint server_count = 0; + uint client_count = 0; + + ServerMap servers; + ClientMap clients; + std::vector server_ids; + + TimePoint early_time; + TimePoint servers_created_time; + TimePoint clients_created_time; + TimePoint clients_finished_time; + TimePoint late_time; + + std::default_random_engine prng; + + bool has_run = false; + + + public: + + double fmt_tp(const TimePoint& t) { + auto c = t.time_since_epoch().count(); + return uint64_t(c / 1000000.0 + 0.5) % 100000 / 1000.0; + } + + TimePoint now() { + return std::chrono::steady_clock::now(); + } + + using ClientBasedServerSelectFunc = + std::function; + + using ClientFilter = std::function; + + using ServerFilter = std::function; + + using ServerDataOutF = + std::function; + + using ClientDataOutF = + std::function; + + Simulation() : + early_time(now()), + prng(std::chrono::system_clock::now().time_since_epoch().count()) + { + // empty + } + + ~Simulation() { + for (auto c : clients) { + TC* cp = c.second; + delete cp; + } + + for (auto s : servers) { + delete s.second; + } + } + + uint get_client_count() const { return client_count; } + uint get_server_count() const { return server_count; } + TC& get_client(ClientId id) { return *clients[id]; } + TS& get_server(ServerId id) { return *servers[id]; } + const ServerId& get_server_id(uint index) const { + return server_ids[index]; + } + + + void add_servers(uint count, + std::function create_server_f) { + uint i = server_count; + + // increment server_count before creating servers since they + // will start running immediately and may use the server_count + // value; NB: this could still be an issue if servers are + // added with multiple add_servers calls; consider using a + // separate start function after all servers (and clients?) + // have been added + server_count += count; + + for (; i < server_count; ++i) { + server_ids.push_back(i); + servers[i] = create_server_f(i); + } + + servers_created_time = now(); + } + + + void add_clients(uint count, + std::function create_client_f) { + uint i = client_count; + + // increment client_count before creating clients since they + // will start running immediately and may use the client_count + // value (e.g., in the server selection function); NB: this could + // still be an issue if clients are added with multiple + // add_clients calls; consider using a separate start function + // after all clients have been added + client_count += count; + + for (; i < client_count; ++i) { + clients[i] = create_client_f(i); + } + + clients_created_time = now(); + } + + + void run() { + assert(server_count > 0); + assert(client_count > 0); + + std::cout << "simulation started" << std::endl; + + // clients are now running; wait for all to finish + + for (auto const &i : clients) { + i.second->wait_until_done(); + } + + late_time = clients_finished_time = now(); + + std::cout << "simulation completed in " << + std::chrono::duration_cast(clients_finished_time - servers_created_time).count() << + " millisecs" << std::endl; + + has_run = true; + } // run + + + void display_stats(std::ostream& out, + ServerDataOutF server_out_f, ClientDataOutF client_out_f, + ServerFilter server_filter = + [] (const ServerId&) { return true; }, + ClientFilter client_filter = + [] (const ClientId&) { return true; }, + int head_w = 12, int data_w = 7, int data_prec = 2) { + assert(has_run); + + // skip first 2 secondsd of data + const std::chrono::seconds skip_amount(0); + // calculate in groups of 5 seconds + const std::chrono::seconds measure_unit(2); + // unit to output reports in + const std::chrono::seconds report_unit(1); + + // compute and display stats + + TimePoint earliest_start = late_time; + TimePoint latest_start = early_time; + TimePoint earliest_finish = late_time; + TimePoint latest_finish = early_time; + + for (auto const &c : clients) { + auto start = c.second->get_op_times().front(); + auto end = c.second->get_op_times().back(); + + if (start < earliest_start) { earliest_start = start; } + if (start > latest_start) { latest_start = start; } + if (end < earliest_finish) { earliest_finish = end; } + if (end > latest_finish) { latest_finish = end; } + } + + double ops_factor = + std::chrono::duration_cast>(measure_unit) / + std::chrono::duration_cast>(report_unit); + + const auto start_edge = clients_created_time + skip_amount; + + std::map> ops_data; + + for (auto const &c : clients) { + auto it = c.second->get_op_times().begin(); + const auto end = c.second->get_op_times().end(); + while (it != end && *it < start_edge) { ++it; } + + for (auto time_edge = start_edge + measure_unit; + time_edge <= latest_finish + measure_unit; + time_edge += measure_unit) { + int count = 0; + for (; it != end && *it < time_edge; ++count, ++it) { /* empty */ } + double ops_per_second = double(count) / ops_factor; + ops_data[c.first].push_back(ops_per_second); + } + } + + out << "==== Client Data ====" << std::endl; + + out << std::setw(head_w) << "client:"; + for (auto const &c : clients) { + if (!client_filter(c.first)) continue; + out << " " << std::setw(data_w) << c.first; + } + out << std::setw(data_w) << "total" << std::endl; + + { + bool has_data; + size_t i = 0; + do { + std::string line_header = "t_" + std::to_string(i) + ":"; + out << std::setw(head_w) << line_header; + has_data = false; + double total = 0.0; + for (auto const &c : clients) { + double data = 0.0; + if (i < ops_data[c.first].size()) { + data = ops_data[c.first][i]; + has_data = true; + } + total += data; + + if (!client_filter(c.first)) continue; + + out << " " << std::setw(data_w) << std::setprecision(data_prec) << + std::fixed << data; + } + out << " " << std::setw(data_w) << std::setprecision(data_prec) << + std::fixed << total << std::endl; + ++i; + } while(has_data); + } + + client_out_f(out, this, client_filter, head_w, data_w, data_prec); + + display_client_internal_stats(out, + "nanoseconds"); + + out << std::endl << "==== Server Data ====" << std::endl; + + out << std::setw(head_w) << "server:"; + for (auto const &s : servers) { + if (!server_filter(s.first)) continue; + out << " " << std::setw(data_w) << s.first; + } + out << " " << std::setw(data_w) << "total" << std::endl; + + server_out_f(out, this, server_filter, head_w, data_w, data_prec); + + display_server_internal_stats(out, + "nanoseconds"); + + // clean up clients then servers + + for (auto i = clients.begin(); i != clients.end(); ++i) { + delete i->second; + i->second = nullptr; + } + + for (auto i = servers.begin(); i != servers.end(); ++i) { + delete i->second; + i->second = nullptr; + } + } // display_stats + + + template + void display_server_internal_stats(std::ostream& out, + std::string time_unit) { + T add_request_time(0); + T request_complete_time(0); + uint32_t add_request_count = 0; + uint32_t request_complete_count = 0; + + for (uint i = 0; i < get_server_count(); ++i) { + const auto& server = get_server(i); + const auto& is = server.get_internal_stats(); + add_request_time += + std::chrono::duration_cast(is.add_request_time); + request_complete_time += + std::chrono::duration_cast(is.request_complete_time); + add_request_count += is.add_request_count; + request_complete_count += is.request_complete_count; + } + + double add_request_time_per_unit = + double(add_request_time.count()) / add_request_count ; + out << "total time to add requests: " << + std::fixed << add_request_time.count() << " " << time_unit << + ";" << std::endl << + " count: " << add_request_count << ";" << std::endl << + " average: " << add_request_time_per_unit << + " " << time_unit << " per request/response" << std::endl; + + double request_complete_time_unit = + double(request_complete_time.count()) / request_complete_count ; + out << "total time to note requests complete: " << std::fixed << + request_complete_time.count() << " " << time_unit << ";" << + std::endl << + " count: " << request_complete_count << ";" << std::endl << + " average: " << request_complete_time_unit << + " " << time_unit << " per request/response" << std::endl; + + out << std::endl; + + assert(add_request_count == request_complete_count); + out << "server timing for QOS algorithm: " << + add_request_time_per_unit + request_complete_time_unit << + " " << time_unit << " per request/response" << std::endl; + } + + + template + void display_client_internal_stats(std::ostream& out, + std::string time_unit) { + T track_resp_time(0); + T get_req_params_time(0); + uint32_t track_resp_count = 0; + uint32_t get_req_params_count = 0; + + for (uint i = 0; i < get_client_count(); ++i) { + const auto& client = get_client(i); + const auto& is = client.get_internal_stats(); + track_resp_time += + std::chrono::duration_cast(is.track_resp_time); + get_req_params_time += + std::chrono::duration_cast(is.get_req_params_time); + track_resp_count += is.track_resp_count; + get_req_params_count += is.get_req_params_count; + } + + double track_resp_time_unit = + double(track_resp_time.count()) / track_resp_count; + out << "total time to track responses: " << + std::fixed << track_resp_time.count() << " " << time_unit << ";" << + std::endl << + " count: " << track_resp_count << ";" << std::endl << + " average: " << track_resp_time_unit << " " << time_unit << + " per request/response" << std::endl; + + double get_req_params_time_unit = + double(get_req_params_time.count()) / get_req_params_count; + out << "total time to get request parameters: " << + std::fixed << get_req_params_time.count() << " " << time_unit << + ";" << std::endl << + " count: " << get_req_params_count << ";" << std::endl << + " average: " << get_req_params_time_unit << " " << time_unit << + " per request/response" << std::endl; + + out << std::endl; + + assert(track_resp_count == get_req_params_count); + out << "client timing for QOS algorithm: " << + track_resp_time_unit + get_req_params_time_unit << " " << + time_unit << " per request/response" << std::endl; + } + + + // **** server selection functions **** + + + const ServerId& server_select_alternate(uint64_t seed, + uint16_t client_idx) { + uint index = (client_idx + seed) % server_count; + return server_ids[index]; + } + + + // returns a lambda using the range specified as servers_per (client) + ClientBasedServerSelectFunc + make_server_select_alt_range(uint16_t servers_per) { + return [servers_per,this](uint64_t seed, uint16_t client_idx) + -> const ServerId& { + double factor = double(server_count) / client_count; + uint offset = seed % servers_per; + uint index = (uint(0.5 + client_idx * factor) + offset) % server_count; + return server_ids[index]; + }; + } + + + // function to choose a server randomly + const ServerId& server_select_random(uint64_t seed, uint16_t client_idx) { + uint index = prng() % server_count; + return server_ids[index]; + } + + + // function to choose a server randomly + ClientBasedServerSelectFunc + make_server_select_ran_range(uint16_t servers_per) { + return [servers_per,this](uint64_t seed, uint16_t client_idx) + -> const ServerId& { + double factor = double(server_count) / client_count; + uint offset = prng() % servers_per; + uint index = (uint(0.5 + client_idx * factor) + offset) % server_count; + return server_ids[index]; + }; + } + + + // function to always choose the first server + const ServerId& server_select_0(uint64_t seed, uint16_t client_idx) { + return server_ids[0]; + } + }; // class Simulation + + }; // namespace qos_simulation +}; // namespace crimson