#include <bits/stdc++.h>
#include "algotester_generator.h"
using namespace std;
struct Query {
int id;
int cpu_time;
int memory;
int max_exec_time;
int query_type;
};
struct Node {
int id;
string node_type;
};
struct User {
int id;
string level;
vector<Node> nodes;
};
struct Server {
int id;
int cpu;
int memory;
int cost_per_second;
};
struct Request {
int timestamp;
int user_id;
int query_id;
int qps;
};
struct Constants {
int min_cpu;
int min_memory;
int max_cpu;
int max_memory;
int granularity_cpu;
int granularity_memory;
int local_scale_time;
int cross_scale_time;
int scale_out_time;
vector<int> customer_weights;
};
struct QueriesConfig {
int count;
vector<int> cpu_range;
vector<int> memory_range;
vector<int> max_time_range;
long double write_ratio;
};
struct UsersConfig {
int count;
vector<string> levels;
vector<int> initial_nodes_range;
};
struct ServersConfig {
int count;
vector<int> cpu_range;
vector<int> memory_range;
vector<int> cost_range;
};
struct BurstConfig {
int interval;
long double multiplier;
int duration;
};
struct RequestsConfig {
int duration;
vector<int> qps_range;
string distribution;
BurstConfig burst_config;
};
struct ConstantsConfig {
int min_cpu;
int min_memory;
int max_cpu;
int max_memory;
int granularity_cpu;
int granularity_memory;
int local_scale_time;
int cross_scale_time;
int scale_out_time;
vector<int> customer_weights;
};
struct Config {
unsigned long long random_seed;
QueriesConfig queries;
UsersConfig users;
ServersConfig servers;
RequestsConfig requests;
ConstantsConfig constants;
};
static const Config kBakedConfig = {
43ULL,
QueriesConfig{50, {10, 30000}, {1, 1000}, {0, 3600}, 0.3L},
UsersConfig{5, {"V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9"}, {1, 5}},
ServersConfig{3, {320000, 1180000}, {64000, 1024000}, {10, 1000}},
RequestsConfig{3600, {1, 200}, "uniform", BurstConfig{300, 3.0L, 60}},
ConstantsConfig{5000, 500, 128000, 131000, 5000, 500, 10, 60, 100, {10, 12, 15, 20, 25, 35, 50, 75, 100}}
};
struct DataGenerator {
Config cfg;
AlgotesterGenerator& gen;
DataGenerator(const Config& cfg_, AlgotesterGenerator& gen_) : cfg(cfg_), gen(gen_) {}
int rand_int(int l, int r) {
return static_cast<int>(gen.randInt(l, r));
}
long double rand_float(long double l, long double r) {
return gen.randDouble(l, r);
}
bool rand_bool(long double p = 0.5L) {
return gen.randDouble() < p;
}
template<typename T>
const T& rand_choice(const vector<T>& v) {
int idx = rand_int(0, static_cast<int>(v.size()) - 1);
return v[idx];
}
int rand_poisson(long double lambda_param) {
long double L = exp(-lambda_param);
int k = 0;
long double p = 1.0L;
while (p > L) {
k++;
p *= gen.randDouble();
}
return k - 1;
}
vector<Server> generate_servers() {
vector<Server> servers;
servers.reserve(cfg.servers.count);
for (int i = 1; i <= cfg.servers.count; i++) {
int cpu = rand_int(cfg.servers.cpu_range[0], cfg.servers.cpu_range[1]);
int memory = rand_int(cfg.servers.memory_range[0], cfg.servers.memory_range[1]);
long double resource_ratio =
((long double)cpu / cfg.servers.cpu_range[1] + (long double)memory / cfg.servers.memory_range[1]) / 2.0L;
long double base_cost =
cfg.servers.cost_range[0] + resource_ratio * (cfg.servers.cost_range[1] - cfg.servers.cost_range[0]);
long double cost_variance = base_cost * 0.2L;
int cost = static_cast<int>(base_cost + rand_float(-cost_variance, cost_variance));
cost = max(cfg.servers.cost_range[0], min(cost, cfg.servers.cost_range[1]));
servers.push_back(Server{i, cpu, memory, cost});
}
return servers;
}
vector<Query> generate_queries(const vector<Server>& servers) {
if (servers.empty()) throw runtime_error("Server list is empty, cannot determine Query memory limit");
int server_max_memory = 0;
for (const auto& s : servers) server_max_memory = max(server_max_memory, s.memory);
vector<Query> queries;
queries.reserve(cfg.queries.count);
for (int i = 1; i <= cfg.queries.count; i++) {
int cpu_time = rand_int(cfg.queries.cpu_range[0], cfg.queries.cpu_range[1]);
int actual_max_memory = min(cfg.queries.memory_range[1], server_max_memory);
int memory = rand_int(cfg.queries.memory_range[0], actual_max_memory);
int max_exec_time = rand_int(cfg.queries.max_time_range[0], cfg.queries.max_time_range[1]);
int query_type = rand_bool(cfg.queries.write_ratio) ? 0 : 1;
queries.push_back(Query{i, cpu_time, memory, max_exec_time, query_type});
}
return queries;
}
vector<User> generate_users() {
vector<User> users;
users.reserve(cfg.users.count);
int current_node_id = 1;
for (int i = 1; i <= cfg.users.count; i++) {
string level = rand_choice(cfg.users.levels);
int node_count = rand_int(cfg.users.initial_nodes_range[0], cfg.users.initial_nodes_range[1]);
vector<Node> nodes;
nodes.reserve(node_count);
for (int j = 0; j < node_count; j++) {
int node_id = current_node_id + j;
nodes.push_back(Node{node_id, j == 0 ? "write" : "read"});
}
users.push_back(User{i, level, move(nodes)});
current_node_id += node_count;
}
return users;
}
vector<Request> generate_requests_at_time(int timestamp, int count,
const vector<int>& qps_range, long double multiplier) {
vector<Request> requests;
requests.reserve(count);
for (int i = 0; i < count; i++) {
const User& user = rand_choice(users_cache);
const Query& query = rand_choice(queries_cache);
int base_qps = rand_int(qps_range[0], qps_range[1]);
int qps = static_cast<int>(base_qps * multiplier);
qps = max(1, min(qps, 10000));
requests.push_back(Request{timestamp, user.id, query.id, qps});
}
return requests;
}
map<int, vector<Request>> generate_requests(const vector<Query>& queries, const vector<User>& users) {
queries_cache = queries;
users_cache = users;
map<int, vector<Request>> requests;
const int duration = cfg.requests.duration;
const vector<int>& qps_range = cfg.requests.qps_range;
if (cfg.requests.distribution == "uniform") {
for (int timestamp = 0; timestamp < duration; timestamp++) {
if (rand_bool(0.7L)) {
int req_count = rand_int(1, 5);
auto reqs = generate_requests_at_time(timestamp, req_count, qps_range, 1.0L);
if (!reqs.empty()) requests[timestamp] = move(reqs);
}
}
} else if (cfg.requests.distribution == "poisson") {
long double lambda_param = (qps_range[0] + qps_range[1]) / 20.0L;
for (int timestamp = 0; timestamp < duration; timestamp++) {
int req_count = rand_poisson(lambda_param);
if (req_count > 0) {
auto reqs = generate_requests_at_time(timestamp, req_count, qps_range, 1.0L);
if (!reqs.empty()) requests[timestamp] = move(reqs);
}
}
} else if (cfg.requests.distribution == "burst") {
int burst_interval = cfg.requests.burst_config.interval;
long double burst_multiplier = cfg.requests.burst_config.multiplier;
int burst_duration = cfg.requests.burst_config.duration;
for (int timestamp = 0; timestamp < duration; timestamp++) {
bool is_burst = (timestamp % burst_interval) < burst_duration;
int req_count = 0;
if (is_burst) {
req_count = rand_int(3, 10);
} else {
if (rand_bool(0.5L)) req_count = rand_int(1, 3);
else continue;
}
auto reqs = generate_requests_at_time(
timestamp, req_count, qps_range, is_burst ? burst_multiplier : 1.0L);
if (!reqs.empty()) requests[timestamp] = move(reqs);
}
} else {
throw runtime_error("Unknown traffic distribution type: " + cfg.requests.distribution);
}
return requests;
}
Constants generate_constants() const {
return Constants{
cfg.constants.min_cpu,
cfg.constants.min_memory,
cfg.constants.max_cpu,
cfg.constants.max_memory,
cfg.constants.granularity_cpu,
cfg.constants.granularity_memory,
cfg.constants.local_scale_time,
cfg.constants.cross_scale_time,
cfg.constants.scale_out_time,
cfg.constants.customer_weights
};
}
void write_servers(ostream& out, const vector<Server>& servers) const {
out << servers.size() << "\n";
for (const auto& s : servers) {
out << s.id << " " << s.cpu << " " << s.memory << " " << s.cost_per_second << "\n";
}
}
void write_queries(ostream& out, const vector<Query>& queries) const {
out << queries.size() << "\n";
for (const auto& q : queries) {
out << q.id << " " << q.cpu_time << " " << q.memory << " " << q.max_exec_time << " " << q.query_type << "\n";
}
}
void write_users(ostream& out, const vector<User>& users) const {
out << users.size() << "\n";
for (const auto& u : users) {
string level_num = u.level;
if (!level_num.empty() && level_num[0] == 'V') level_num = level_num.substr(1);
out << u.id << " " << level_num << " " << u.nodes.size();
for (size_t i = 0; i < u.nodes.size(); i++) {
out << " " << u.nodes[i].id;
}
out << "\n";
}
}
void write_requests(ostream& out, const map<int, vector<Request>>& requests) const {
out << requests.size() << "\n";
for (const auto& [ts, reqs] : requests) {
bool first = true;
for (const auto& r : reqs) {
if (!first) out << " ";
if (first) out << r.timestamp << " " << r.user_id << " " << r.query_id << " " << r.qps;
else out << r.user_id << " " << r.query_id << " " << r.qps;
first = false;
}
out << "\n";
}
}
void write_constants(ostream& out, const Constants& c) const {
out << c.min_cpu << " " << c.min_memory << "\n";
out << c.max_cpu << " " << c.max_memory << "\n";
out << c.granularity_cpu << " " << c.granularity_memory << "\n";
out << c.local_scale_time << "\n";
out << c.cross_scale_time << "\n";
out << c.scale_out_time << "\n";
for (size_t i = 0; i < c.customer_weights.size(); i++) {
if (i) out << " ";
out << c.customer_weights[i];
}
out << "\n";
}
private:
vector<Query> queries_cache;
vector<User> users_cache;
};
int main(int argc, char* argv[]) {
try {
Config cfg = kBakedConfig;
AlgotesterGenerator gen(argc, argv);
DataGenerator generator(cfg, gen);
auto servers = generator.generate_servers();
auto queries = generator.generate_queries(servers);
auto users = generator.generate_users();
auto requests = generator.generate_requests(queries, users);
auto constants = generator.generate_constants();
generator.write_servers(cout, servers);
generator.write_queries(cout, queries);
generator.write_users(cout, users);
generator.write_requests(cout, requests);
generator.write_constants(cout, constants);
} catch (const exception& e) {
cerr << "Error: " << e.what() << "\n";
return 1;
}
return 0;
}