From 070ce923574dcc57435cb3fb2dfe86b6a38cd249 Mon Sep 17 00:00:00 2001 From: MDBijman Date: Tue, 24 Jan 2017 12:15:26 +0100 Subject: Initial code commit with organized dependencies --- Simulator/include/Simulator.h | 139 ++++++++++++++++ Simulator/include/database/Database.h | 74 +++++++++ Simulator/include/database/Queries.h | 131 +++++++++++++++ Simulator/include/database/Query.h | 124 ++++++++++++++ Simulator/include/database/QueryExecuter.h | 155 ++++++++++++++++++ Simulator/include/database/QueryResult.h | 25 +++ Simulator/include/modeling/Datacenter.h | 40 +++++ Simulator/include/modeling/Entity.h | 12 ++ Simulator/include/modeling/ModelingTypes.h | 7 + Simulator/include/modeling/Rack.h | 33 ++++ Simulator/include/modeling/Room.h | 52 ++++++ Simulator/include/modeling/TypeIndex.h | 9 ++ Simulator/include/modeling/machine/CPU.h | 34 ++++ Simulator/include/modeling/machine/GPU.h | 33 ++++ Simulator/include/modeling/machine/Machine.h | 112 +++++++++++++ Simulator/include/simulation/Experiment.h | 179 +++++++++++++++++++++ Simulator/include/simulation/Path.h | 63 ++++++++ Simulator/include/simulation/Section.h | 76 +++++++++ Simulator/include/simulation/history/History.h | 43 +++++ .../include/simulation/history/MachineSnapshot.h | 17 ++ .../include/simulation/history/SimulationHistory.h | 81 ++++++++++ .../include/simulation/history/WorkloadSnapshot.h | 13 ++ .../schedulers/FirstInFirstOutScheduler.h | 37 +++++ .../include/simulation/schedulers/Scheduler.h | 25 +++ .../schedulers/ShortestRemainingTimeScheduler.h | 46 ++++++ Simulator/include/simulation/workloads/Workload.h | 80 +++++++++ .../include/simulation/workloads/WorkloadPool.h | 43 +++++ 27 files changed, 1683 insertions(+) create mode 100644 Simulator/include/Simulator.h create mode 100644 Simulator/include/database/Database.h create mode 100644 Simulator/include/database/Queries.h create mode 100644 Simulator/include/database/Query.h create mode 100644 Simulator/include/database/QueryExecuter.h create mode 100644 Simulator/include/database/QueryResult.h create mode 100644 Simulator/include/modeling/Datacenter.h create mode 100644 Simulator/include/modeling/Entity.h create mode 100644 Simulator/include/modeling/ModelingTypes.h create mode 100644 Simulator/include/modeling/Rack.h create mode 100644 Simulator/include/modeling/Room.h create mode 100644 Simulator/include/modeling/TypeIndex.h create mode 100644 Simulator/include/modeling/machine/CPU.h create mode 100644 Simulator/include/modeling/machine/GPU.h create mode 100644 Simulator/include/modeling/machine/Machine.h create mode 100644 Simulator/include/simulation/Experiment.h create mode 100644 Simulator/include/simulation/Path.h create mode 100644 Simulator/include/simulation/Section.h create mode 100644 Simulator/include/simulation/history/History.h create mode 100644 Simulator/include/simulation/history/MachineSnapshot.h create mode 100644 Simulator/include/simulation/history/SimulationHistory.h create mode 100644 Simulator/include/simulation/history/WorkloadSnapshot.h create mode 100644 Simulator/include/simulation/schedulers/FirstInFirstOutScheduler.h create mode 100644 Simulator/include/simulation/schedulers/Scheduler.h create mode 100644 Simulator/include/simulation/schedulers/ShortestRemainingTimeScheduler.h create mode 100644 Simulator/include/simulation/workloads/Workload.h create mode 100644 Simulator/include/simulation/workloads/WorkloadPool.h (limited to 'Simulator/include') diff --git a/Simulator/include/Simulator.h b/Simulator/include/Simulator.h new file mode 100644 index 00000000..381639af --- /dev/null +++ b/Simulator/include/Simulator.h @@ -0,0 +1,139 @@ +#pragma once +#include "simulation/Section.h" +#include "database/Database.h" + +#include +#include + +namespace Simulation +{ + /* + The Simulator class controls (the creation of) all experiments, and providing access to the database. + */ + template + class Simulator + { + public: + /* + Initializes the simulator with an empty list of experiments, and with a reference to the database. + */ + explicit Simulator(char* databaseName) : database(databaseName) {} + + /* + Adds a simulation to the list of experiments from the database, removing it from the queue + of experiments in the database. + */ + void load(int experimentId) + { + Experiment experiment = database.createExperiment(experimentId); + experiments.insert(std::make_pair(experimentId, experiment)); + database.dequeueExperiment(experimentId); + } + + /* + Polls the database for new jobs and simulates every queued simulation it finds. + */ + void pollAndLoadAll() + { + int rc = pollAndLoad(); + if (rc != -1) + pollAndLoadAll(); + } + + /* + Polls the database for new jobs and simulates the first it finds. + */ + int pollAndLoad() + { + int id = database.pollQueuedExperiments(); + if (id != -1) + { + std::cout << "Loaded simulation section " << id << std::endl; + load(id); + } + return id; + } + + /* + Writes the state of all experiments if their history is size 3000 or larger. + */ + void writeHistoryAll() + { + if (experiments.size() == 0) + return; + + auto it = experiments.begin(); + while(it != experiments.end()) + { + auto history = (*it).second.getHistory(); + if (history.historySize() > 3000 || (*it).second.isFinished()) + write((*it).first); + + if ((*it).second.isFinished()) + { + std::cout << "Finished simulation." << std::endl; + it = experiments.erase(it); + } + else + { + ++it; + } + } + } + + /* + Writes the state of the given simulation to the database. + */ + void write(int id) + { + std::cout << "Writing batch." << std::endl; + database.startTransaction(); + database.writeExperimentHistory(experiments.at(id)); + database.endTransaction(); + auto history = experiments.at(id).getHistory(); + history.clearHistory(); + std::cout << "Finished writing batch." << std::endl; + } + + /* + Ticks each simulation once. + */ + void tickAll() + { + for (std::pair& s : experiments) + s.second.tick(); + } + + /* + Ticks the given simulation once. + */ + void tick(int simulationId) + { + experiments.at(simulationId).tick(); + } + + /* + Returns true if all experiments are finished. + */ + bool hasSimulations() const + { + return experiments.size() != 0; + } + + /* + Saves the state of all workloads to the history. + */ + void saveStateAll() + { + for (auto& pair : experiments) + pair.second.saveState(); + } + + private: + // The database to write results to. + Database::Database database; + + // The list of experiments. + std::unordered_map experiments; + }; +} diff --git a/Simulator/include/database/Database.h b/Simulator/include/database/Database.h new file mode 100644 index 00000000..6e8bfeef --- /dev/null +++ b/Simulator/include/database/Database.h @@ -0,0 +1,74 @@ +#pragma once +#include "simulation/Section.h" +#include "modeling/ModelingTypes.h" + +#include +#include "simulation/Experiment.h" + +namespace Database +{ + /* + The Database class provides a wrapper for the sqlite interface. + */ + class Database + { + public: + /* + Initializes a database with the given name. If it does not yet exist is creates a $name.db file. + */ + explicit Database(char* name); + + /* + Closes the database connection. + */ + ~Database(); + + /* + Starts a sqlite transaction. + */ + void startTransaction() const; + + /* + Ends a sqlite transaction. + */ + void endTransaction() const; + + /* + Writes the history of the experiment to the database. + */ + void writeExperimentHistory(Simulation::Experiment& simulation) const; + + /* + Polls the database for new simulation sections to simulate. + If there are no rows in the table, this returns -1. + */ + int pollQueuedExperiments() const; + + /* + Removes a row of the queued simulation sections. + */ + void dequeueExperiment(int id) const; + + + /* + Creates a simulation object from a simulation in the database. + */ + Simulation::Experiment createExperiment(uint32_t id); + + private: + /* + Sets the scheduler of the datacenter + */ + Simulation::Scheduler* loadScheduler(uint32_t simulationSectionId) const; + + DefaultDatacenter loadDatacenter(uint32_t datacenterId) const; + + /* + Fills the datacenter with workloads from the database. + */ + Simulation::WorkloadPool loadWorkloads(uint32_t traceId) const; + + // The sqlite db connection. + sqlite3 *db; + }; +} diff --git a/Simulator/include/database/Queries.h b/Simulator/include/database/Queries.h new file mode 100644 index 00000000..73afc1a1 --- /dev/null +++ b/Simulator/include/database/Queries.h @@ -0,0 +1,131 @@ +#pragma once +#include "Query.h" + +namespace Database +{ + namespace Queries + { + Query GET_QUEUED_EXPERIMENTS(std::string(R"query( + SELECT experiment_id FROM queued_experiments; + )query")); + + Query<> REMOVE_QUEUED_EXPERIMENT(std::string(R"query( + DELETE FROM queued_experiments WHERE experiment_id = $id; + )query")); + + Query GET_EXPERIMENT_BY_ID(std::string(R"query( + SELECT id, simulation_id, path_id, trace_id, scheduler_name, name FROM experiments WHERE id = $id; + )query")); + + Query GET_PATH_BY_ID(std::string(R"query( + SELECT id, simulation_id, name, datetime_created FROM paths WHERE id = $id; + )query")); + + Query GET_SECTION_BY_PATH_ID(std::string(R"query( + SELECT id, path_id, datacenter_id, start_tick FROM sections WHERE path_id = $id; + )query")); + + + /* + Returns the type of the scheduler of the given simulation section. + Returns: + Binds: + */ + Query GET_SCHEDULER_TYPE_OF_EXPERIMENT(std::string(R"query( + SELECT scheduler_name FROM experiments WHERE id = $id; + )query")); + + /* + Returns the id of the trace of the given simulation section. + Returns: + Binds: + */ + Query GET_TRACE_OF_EXPERIMENT(std::string(R"query( + SELECT trace_id FROM experiments WHERE id = $id; + )query")); + + /* + Returns all columns of each room belonging to the given datacenter. + Returns: + Binds: + */ + Query GET_ROOMS_OF_DATACENTER(std::string(R"query( + SELECT * FROM rooms WHERE datacenter_id = $id; + )query")); + + /* + Returns all columns of each rack belonging to the given room. + Returns: + Binds: + */ + Query GET_RACKS_OF_ROOM(std::string(R"query( + SELECT racks.* FROM tiles, objects, racks + WHERE objects.id = tiles.object_id + AND objects.id = racks.id + AND tiles.room_id = $id; + )query")); + + /* + Returns the machine in a given rack. + Returns: + Binds: + */ + Query GET_MACHINES_OF_RACK(std::string(R"query( + SELECT id, position FROM machines + WHERE rack_id = $rid; + )query")); + + /* + Returns all columns of each task belonging to the given trace. + Returns: + Binds: + */ + Query GET_TASKS_OF_TRACE(std::string(R"query( + SELECT * FROM tasks WHERE trace_id = $id; + )query")); + + /* + Returns the information of each cpu in the given rack, and their corresponding machine. + Returns: + Binds: + */ + Query GET_CPUS_IN_RACK(std::string(R"query( + SELECT machines.position AS slot, cpus.clock_rate_mhz AS machine_speed, cpus.number_of_cores AS cores, cpus.energy_consumption_w AS energy_consumption, cpus.failure_model_id AS failure_model_id FROM cpus, machine_cpus, machines + WHERE machine_cpus.cpu_id = cpus.id + AND machine_cpus.machine_id = machines.id + AND machines.rack_id = $id; + )query")); + + /* + Returns the information of each gpu in the given rack, and their corresponding machine. + Returns: + Binds: + */ + Query GET_GPUS_IN_RACK(std::string(R"query( + SELECT machines.position AS slot, gpus.clock_rate_mhz AS speed, gpus.number_of_cores AS cores, gpus.energy_consumption_w AS energy_consumption, gpus.failure_model_id AS failure_model_id FROM gpus, machine_gpus, machines + WHERE machine_gpus.gpu_id = gpus.id + AND machine_gpus.machine_id = machines.id + AND machines.rack_id = $id; + )query")); + + /* + Inserts the state of a workload into the task_state table. + Returns: <> + Binds: + */ + Query<> WRITE_WORKLOAD_STATE(std::string(R"query( + INSERT INTO task_states (task_id, experiment_id, tick, flops_left) + VALUES ($tid, $ssid, $tick, $flops); + )query")); + + /* + Inserts the state of a machine into the machine_state table. + Returns: <> + Binds: + */ + Query<> WRITE_MACHINE_STATE(std::string(R"query( + INSERT INTO machine_states (task_id, machine_id, experiment_id, tick, temperature_c, in_use_memory_mb, load_fraction) + VALUES ($tid, $mid, $ssid, $tick, $temp, $mem, $load); + )query")); + } +} diff --git a/Simulator/include/database/Query.h b/Simulator/include/database/Query.h new file mode 100644 index 00000000..46ce0ee2 --- /dev/null +++ b/Simulator/include/database/Query.h @@ -0,0 +1,124 @@ +#pragma once +#include +#include + +namespace Database +{ + + namespace Specializations + { + template + ReturnType getResult(int location, sqlite3_stmt* statement); + + template<> + inline int getResult(int location, sqlite3_stmt* statement) + { + return sqlite3_column_int(statement, location); + } + + template<> + inline std::string getResult(int location, sqlite3_stmt* statement) + { + return std::string(reinterpret_cast(sqlite3_column_text(statement, location))); + } + + template + void bind(ValueType value, int location, sqlite3_stmt* statement); + + template<> + inline void bind(int value, int location, sqlite3_stmt* statement) + { + int rc = sqlite3_bind_int(statement, location, value); + assert(rc == SQLITE_OK); + } + + template<> + inline void bind(float value, int location, sqlite3_stmt* statement) + { + int rc = sqlite3_bind_double(statement, location, static_cast(value)); + assert(rc == SQLITE_OK); + } + } + + + + template + class Query + { + public: + explicit Query(std::string query) : statement(nullptr), content(query) + {} + + /* + Calls sqlite3_finalize on the statement. + */ + ~Query() + { + int rc = sqlite3_finalize(statement); + assert(rc == SQLITE_OK); + } + + /* + Calls sqlite3_prepare_v2 to prepare this query. + */ + void prepare(sqlite3* db) + { + // Preparation of the statement. + int rc = sqlite3_prepare_v2(db, content.c_str(), static_cast(content.size()), &statement, NULL); + assert(rc == SQLITE_OK); + } + + /* + Steps the execution of this query once. Returns true if the return code is SQLITE_ROW. + */ + bool step() const + { + // Execution of the statement + int rc = sqlite3_step(statement); + if(rc == SQLITE_ROW) + return true; + if(rc == SQLITE_DONE) + return false; + + assert(!"The return code of step was not SQLITE_ROW (100) or SQLITE_DONE (101)!"); + return false; + } + + /* + Resets this query back to its initial state. + */ + void reset() const + { + sqlite3_reset(statement); + } + + /* + A template for implementing the binding of values to parameters in the sqlite statement. + */ + template + void bind(ValueType value, int location) + { + Specializations::bind(value, location, statement); + } + + /** + * \brief Returns the result of ReturnType at the given location in the query result row. + * \tparam ReturnType The type of the entry in the row. + * \param location The index of the entry in the row. + * \return The result of the query at the given location. + */ + template + ReturnType getResult(int location) + { + return Specializations::getResult(location, statement); + } + + private: + // The sqlite3 statement that corresponds to this query. + sqlite3_stmt* statement; + // The sql string that will be executed. + std::string content; + + }; + +} diff --git a/Simulator/include/database/QueryExecuter.h b/Simulator/include/database/QueryExecuter.h new file mode 100644 index 00000000..d66f9e85 --- /dev/null +++ b/Simulator/include/database/QueryExecuter.h @@ -0,0 +1,155 @@ +#pragma once +#include "Query.h" +#include "QueryResult.h" + +#include +#include +#include +#include + +namespace Database +{ + template + class QueryExecuter + { + public: + /* + Creates a prepared statement and initializes the query. + */ + explicit QueryExecuter(sqlite3* db) : database(db) {} + + /* + Sets the query this executer will execute. + */ + QueryExecuter& setQuery(Query query) + { + this->query = std::make_unique>(query); + this->query->prepare(database); + return *this; + } + + /* + Binds the given name-value pairs to the statement. + Recursive case. + */ + template + QueryExecuter& bindParams(BindType locationValuePair, int depth = 1) + { + query->template bind(locationValuePair, depth); + return *this; + } + + /* + Binds the given name-value pairs to the statement. + Recursive case. + */ + template + QueryExecuter& bindParams(BindType locationValuePair, BindTypes... locationValuePairs, int depth = 1) + { + query->template bind(locationValuePair, depth); + bindParams(locationValuePairs..., depth + 1); + return *this; + } + + /* + Executes the query and returns a tuple with an entry for each type. Use this to recieve only the first result. + Multiple calls, or calls when there are no rows in the db, can lead to exceptions. + This is functionally equivalent to stepping and then calling getResult + */ + QueryResult executeOnce() + { + query->step(); + return QueryResult(getResult()); + } + + /* + Steps the query. + */ + bool step() + { + return query->step(); + } + + /* + Returns the result. + */ + QueryResult result() + { + return QueryResult(getResult()); + } + + /* + Executes the query and returns a vector of tuples for each result. + */ + std::vector> execute(int limit = 0) + { + std::vector> result; + + // Return code + bool more; + + int limiter = 0; + + do + { + // Execution of the statement + more = query->step(); + + if (!more || (limiter >= limit && limit > 0)) + break; + + limiter++; + std::tuple row = getResult(); + result.emplace_back(row); + + } while (more); + + return result; + } + + /* + Resets the sqlite3 query object. + */ + QueryExecuter& reset() + { + query->reset(); + return *this; + } + + private: + /* + Returns the results after executing the query. + Base case. + */ + template + std::tuple getResult(int depth = 0) + { + return std::tuple(query->template getResult(depth)); + } + + /* + Returns the results after executing the query. + Recursive Case. + */ + template + std::tuple getResult(int depth = 0) + { + std::tuple first = std::tuple(query->template getResult(depth)); + std::tuple rest = getResult(depth + 1); + return std::tuple_cat(first, rest); + } + + /* + Returns an empty tuple for when there are no return values. + */ + template + std::tuple<> getResult(int depth = 0) const + { + return std::tuple<>(); + } + + std::unique_ptr> query; + sqlite3* database; + }; +} + diff --git a/Simulator/include/database/QueryResult.h b/Simulator/include/database/QueryResult.h new file mode 100644 index 00000000..c1534be3 --- /dev/null +++ b/Simulator/include/database/QueryResult.h @@ -0,0 +1,25 @@ +#pragma once +#include + +namespace Database +{ + template + class QueryResult + { + public: + explicit QueryResult(ReturnTypes... returnValues) : values(returnValues...) {} + explicit QueryResult(std::tuple returnValues) : values(returnValues) {} + + /* + Returns the item at the given index. + ReturnType must be the same as the type of the item at position Index in the tuple. + */ + template + ReturnType get() + { + return std::get(values); + } + + std::tuple values; + }; +} diff --git a/Simulator/include/modeling/Datacenter.h b/Simulator/include/modeling/Datacenter.h new file mode 100644 index 00000000..a9558f0a --- /dev/null +++ b/Simulator/include/modeling/Datacenter.h @@ -0,0 +1,40 @@ +#pragma once +#include "modeling/Room.h" +#include "simulation/schedulers/Scheduler.h" +#include "modeling/TypeIndex.h" + +#include + +namespace Modeling +{ + /* + The Datacenter class models a datacenter with rooms/entities. + */ + template + class Datacenter + { + public: + /* + Returns a reference to the vector of rooms in this datacenter. + */ + template + std::vector& getRoomsOfType() + { + return std::get::value>(rooms); + } + + /* + Adds a room to this datacenter. + */ + template + void addRoomOfType(RoomType& room) + { + std::get::value>(rooms).push_back(std::move(room)); + } + + + private: + // A vector of rooms that are part of this datacenter. + std::tuple...> rooms; + }; +} diff --git a/Simulator/include/modeling/Entity.h b/Simulator/include/modeling/Entity.h new file mode 100644 index 00000000..c8c5a7cd --- /dev/null +++ b/Simulator/include/modeling/Entity.h @@ -0,0 +1,12 @@ +#pragma once +namespace Modeling +{ + class Entity + { + public: + explicit Entity(int id); + + // The id of this entity in the database + int id; + }; +} diff --git a/Simulator/include/modeling/ModelingTypes.h b/Simulator/include/modeling/ModelingTypes.h new file mode 100644 index 00000000..611a3653 --- /dev/null +++ b/Simulator/include/modeling/ModelingTypes.h @@ -0,0 +1,7 @@ +#pragma once + +// Define a DefaultDC type, capable of holding server rooms. +using DefaultDatacenter = Modeling::Datacenter; + +// Define a type of simulation, capable of simulating the DefaultDC. +using DefaultSection = Simulation::Section; \ No newline at end of file diff --git a/Simulator/include/modeling/Rack.h b/Simulator/include/modeling/Rack.h new file mode 100644 index 00000000..7e5638ef --- /dev/null +++ b/Simulator/include/modeling/Rack.h @@ -0,0 +1,33 @@ +#pragma once +#include "modeling/Entity.h" +#include "modeling/machine/Machine.h" + +#include + +namespace Modeling +{ + /* + The Rack class models a physical rack. It holds a vector of machines. + */ + class Rack : public Entity + { + public: + /* + Initializes the rack with the given machines. + */ + Rack(int id, std::unordered_map machines); + + /* + Returns all machines in this rack. + */ + std::unordered_map& getMachines(); + + /* + Returns the machine at the given slot. + */ + Machine& getMachineAtSlot(int slot); + + private: + std::unordered_map machines; + }; +} diff --git a/Simulator/include/modeling/Room.h b/Simulator/include/modeling/Room.h new file mode 100644 index 00000000..a57b045e --- /dev/null +++ b/Simulator/include/modeling/Room.h @@ -0,0 +1,52 @@ +#pragma once +#include "modeling/Rack.h" +#include "modeling/TypeIndex.h" + +#include + +namespace Modeling +{ + /* + The Room class models the rooms that can be created in the simulation. It contains a list of all entities in the room. + */ + template + class Room + { + //static_assert(std::is_base_of..., "Each type must be derived from Entity!"); + public: + /* + Initializes the room with the given name. + */ + explicit Room(int id) : id(id) {} + + /* + Adds the entity to the list of entities in this room. + */ + template + void addEntity(EntityType& e) + { + std::get::value>(entities).push_back(e); + } + + /* + Returns all entities of the given type. + */ + template + std::vector& getEntitiesOfType() + { + return std::get::value>(entities); + } + + // The id of this room corresponding to its id in the database. + const int id; + + private: + // A vector for each type of entity + std::tuple...> entities; + }; + + using ServerRoom = Room; + using Hallway = Room<>; + using PowerRoom = Room<>; + +} diff --git a/Simulator/include/modeling/TypeIndex.h b/Simulator/include/modeling/TypeIndex.h new file mode 100644 index 00000000..6fcda550 --- /dev/null +++ b/Simulator/include/modeling/TypeIndex.h @@ -0,0 +1,9 @@ +#pragma once +template struct indexOfType; + +template +struct indexOfType : std::integral_constant {}; + +template +struct indexOfType : std::integral_constant::value> {}; + diff --git a/Simulator/include/modeling/machine/CPU.h b/Simulator/include/modeling/machine/CPU.h new file mode 100644 index 00000000..dce4d2c5 --- /dev/null +++ b/Simulator/include/modeling/machine/CPU.h @@ -0,0 +1,34 @@ +#pragma once + +namespace Modeling +{ + class CPU + { + public: + CPU(int speed, int cores, int energyConsumption, int failureModelId); + + /* + Returns the speed of this CPU. + */ + int getSpeed(); + + /* + Returns the nr of cores of this CPU. + */ + int getCores(); + + /* + Returns the energy consumed by this CPU. + */ + int getEnergyConsumption(); + + /* + Returns the failure model id of this CPU. + */ + int getFailureModelId(); + + + private: + int speed, cores, energyConsumption, failureModelId; + }; +} diff --git a/Simulator/include/modeling/machine/GPU.h b/Simulator/include/modeling/machine/GPU.h new file mode 100644 index 00000000..049b928e --- /dev/null +++ b/Simulator/include/modeling/machine/GPU.h @@ -0,0 +1,33 @@ +#pragma once + +namespace Modeling +{ + class GPU + { + public: + GPU(int speed, int cores, int energyConsumption, int failureModelId); + + /* + Returns the speed of this CPU. + */ + int getSpeed(); + + /* + Returns the nr of cores of this CPU. + */ + int getCores(); + + /* + Returns the energy consumed by this CPU. + */ + int getEnergyConsumption(); + + /* + Returns the failure model id of this CPU. + */ + int getFailureModelId(); + + private: + int speed, cores, energyConsumption, failureModelId; + }; +} diff --git a/Simulator/include/modeling/machine/Machine.h b/Simulator/include/modeling/machine/Machine.h new file mode 100644 index 00000000..c89d32d1 --- /dev/null +++ b/Simulator/include/modeling/machine/Machine.h @@ -0,0 +1,112 @@ +#pragma once +#include "simulation/workloads/Workload.h" +#include "modeling/machine/CPU.h" +#include "modeling/machine/GPU.h" + +#include +#include +#include + +namespace Modeling +{ + // Defines the initial temperature of machine + constexpr float ROOM_TEMPERATURE_CELCIUS = 23.0f; + + // Defines the usage of memory by the kernel + constexpr uint32_t KERNEL_MEMORY_USAGE_MB = 50; + + /* + The Machine class models a physical machine in a rack. It has a speed, and can be given a workload on which it will work until finished or interrupted. + */ + class Machine + { + public: + /* + Initializes the machine as idle with the given speed. + */ + Machine(int id); + + /* + Adds a cpu to the list of this machine. + */ + void addCPU(CPU cpu); + + /* + Adds a cpu to the list of this machine. + */ + void addGPU(GPU gpu); + + /* + Gives the task to this machine. If the machine is already busy this does nothing. + */ + void giveTask(Simulation::Workload* workload); + + /* + Returns true if the machine is busy. + */ + bool isBusy() const; + + /* + Does work on the given task and updates temperature and load appropriately. + */ + void work(); + + /* + Returns the id of the current workload of this machine. + */ + int getWorkloadId() const; + + /* + Returns the id of this machine. + */ + int getId() const; + + /* + Returns the temperature of this machine. + */ + float getTemperature() const; + + /* + Returns the memory used by this machine. + */ + int getMemory() const; + + /* + Returns the load fraction on this machine. + */ + float getLoad() const; + + private: + // A list of cpus in this machine. + std::vector cpus; + + // A list of gpus in this machine. + std::vector gpus; + + // True if the machine is working on a task. + bool busy = false; + + // The current workload the machine is working on. + Simulation::Workload* currentWorkload; + + // Db id of this machine. + int id; + + // Temperature of this machine. + float temperature = ROOM_TEMPERATURE_CELCIUS; + float maxTemperature = 80.0f; + float minTemperature = 0.0f; + float temperatureIncrease = 10.f; + + // Memory used by this machine. + int memory = KERNEL_MEMORY_USAGE_MB; + + // The fraction of load on this machine. + float load = 0.0f; + + /* + Returns the speed of the machine. + */ + uint32_t getSpeed(); + }; +} diff --git a/Simulator/include/simulation/Experiment.h b/Simulator/include/simulation/Experiment.h new file mode 100644 index 00000000..69c192fa --- /dev/null +++ b/Simulator/include/simulation/Experiment.h @@ -0,0 +1,179 @@ +#pragma once +#include "Path.h" +#include +#include "workloads/WorkloadPool.h" + +namespace Simulation +{ + /** + * \brief Holds a Path, Scheduler, and WorkloadPool together to form a single unit that can be simulated. + */ + class Experiment + { + public: + /** + * \brief Instantiates a new, complete, experiment that starts at tick 0 and can be simulated. + * \param path The path this experiment should simulate. + * \param scheduler The scheduler this experiment should use for workload balancing. + * \param pool The workloadPool that contains the workloads of this experiment. + * \param id The id of this experiment as it is in the database. + */ + Experiment(Path path, Scheduler* scheduler, WorkloadPool pool, uint32_t id) : path(path), scheduler(scheduler), id(id), currentTick(0), workloadPool(pool) + {} + + /** + * \brief Simulates a single tick of this experiment. + */ + void tick() + { + if(finished) return; + + workloadPool.clearFinishedWorkloads(); + + auto machineAccumulator = path.getCurrentSection(currentTick).getMachines(); + + // Schedule the workload over each machine + scheduler->schedule(machineAccumulator, workloadPool.getWorkloads(currentTick)); + + // Update each machine + std::for_each( + machineAccumulator.begin(), + machineAccumulator.end(), + [this](const std::reference_wrapper& machineWrapper) { + machineWrapper.get().work(); + } + ); + + currentTick++; + + if(workloadPool.isEmpty()) finished = true; + } + + /** + * \brief Saves the state of the simulation, adding it to the history. + */ + void saveState() + { + for(Workload* workload : workloadPool.getWorkloads(currentTick)) + { + history.addSnapshot( + currentTick, + WorkloadSnapshot( + workload->getId(), + workload->getRemainingOperations() + ) + ); + } + + for(std::reference_wrapper machineref : path.getCurrentSection(currentTick).getMachines()) + { + auto machine = machineref.get(); + history.addSnapshot( + currentTick, + MachineSnapshot( + machine.getId(), + machine.getWorkloadId(), + machine.getTemperature(), + machine.getLoad(), + machine.getMemory() + ) + ); + } + } + + /** + * \brief Adds the given workload to the pool of workloads of this simulation. + * \param wl The workload to add to the simulator of this experiment. + */ + void addWorkload(Workload& wl) + { + workloadPool.addWorkload(wl); + } + + /** + * \return A reference to the workloads of this simulation. + */ + WorkloadPool& getWorkloadPool() + { + return workloadPool; + } + + /** + * \return The current tick which is being simulated, i.e. the number of ticks that have passed. + */ + uint32_t getCurrentTick() const + { + return currentTick; + } + + /** + * \return The history of this experiment that has no yet been written to the database. + */ + SimulationHistory getHistory() const + { + return history; + } + + /** + * \return The id of this experiment as it is in the database. + */ + uint32_t getId() const + { + return id; + } + + /** + * \brief Sets this experiment to finished. After calling this method, + * the tick() method will have no effect. + */ + void end() + { + this->finished = true; + } + + /** + * \return True if the experiment is finished, i.e. when all workloads have been completed. + */ + bool isFinished() const + { + return this->finished; + } + + private: + /** + * \brief The path of this experiment which contains the sections and when they should be used. + */ + Path path; + + /** + * \brief The scheduler that this used for workload balancing. + */ + std::shared_ptr scheduler; + + /** + * \brief The id of this experiment as it is in the database. + */ + uint32_t id; + + /** + * \brief The number of ticks that have passed. + */ + uint32_t currentTick; + + /** + * \brief The pool of workloads in this simulation, to be distributed by the scheduler. + */ + WorkloadPool workloadPool; + + /** + * \brief The part of the history of this simulation which has not been written to the database. + */ + SimulationHistory history; + + /** + * \brief If this is true, then tick will not do anything. This indicates that the simulation is finished, + * and that its history should be written to disk. + */ + bool finished = false; + }; +} diff --git a/Simulator/include/simulation/Path.h b/Simulator/include/simulation/Path.h new file mode 100644 index 00000000..d35d49fc --- /dev/null +++ b/Simulator/include/simulation/Path.h @@ -0,0 +1,63 @@ +#pragma once +#include "Section.h" +#include "modeling/ModelingTypes.h" + +namespace Simulation +{ + /** + * \brief Holds all sections of the parent experiment, and returns the correct one + * based on the current tick. + */ + class Path + { + public: + explicit Path(int id) : id(id) + {} + + + /** + * \brief Adds the given section to this path. The begin tick of this section + * should not already be in use by one of the other sections in this path. + * \param section The section to add to this path. + */ + void addSection(DefaultSection section) + { + sections.push_back(section); + } + + /** + * \brief Returns the section that is currently in use by taking the section + * that has the greatest begin tick smaller than the current tick + * \param currentTick The tick the simulator is simulating right now. + * \return The section that is currently in use. + */ + DefaultSection& getCurrentSection(uint32_t currentTick) + { + size_t currentSection = 0; + + uint32_t currentStartTick = 0; + for(int i = 0; i < sections.size(); ++i) + { + uint32_t tempStartTick = sections.at(i).getStartTick(); + if(tempStartTick > currentStartTick && tempStartTick < currentTick) + currentSection = i; + } + + return sections.at(currentSection); + } + + private: + + /** + * \brief The unordered vector of sections in this path. No pair of sections + * should share the same begin tick. + */ + std::vector sections; + + + /** + * \brief The id of this path as it is in the database. + */ + int id; + }; +} diff --git a/Simulator/include/simulation/Section.h b/Simulator/include/simulation/Section.h new file mode 100644 index 00000000..3c11b073 --- /dev/null +++ b/Simulator/include/simulation/Section.h @@ -0,0 +1,76 @@ +#pragma once +#include "modeling/Datacenter.h" +#include "modeling/Room.h" +#include "simulation/history/SimulationHistory.h" + +#include +#include + +namespace Simulation +{ + /** + * \brief Holds a datacenter and the tick on which the parent experiment should switch to this section. + * \tparam DatacenterType The type of datacenter to be used. + */ + template + class Section + { + public: + /** + * \brief Initializes the datacenter in the simulation. Sets paused to false and finished to false. + * \param dc The topology of this section. + * \param startTick The tick on which the experiment should start using the topology of this section. + */ + Section(DatacenterType& dc, uint32_t startTick) : datacenter(dc), startTick(startTick) + {} + + /** + * \return A reference to the datacenter of this section. + */ + DatacenterType& getDatacenter() + { + return datacenter; + } + + /** + * \return All machines in the datacenter of section. + */ + std::vector> getMachines() + { + using namespace std; + + vector> machineAccumulator; + + // For each serverroom, we get the racks in the room + vector& rooms = datacenter.template getRoomsOfType(); + for(auto& room : rooms) + // For each rack get the machines inside that rack + for(auto& rack : room.getEntitiesOfType()) + // Add each machine to the accumulator + for(auto& machine : rack.getMachines()) + machineAccumulator.push_back(ref(machine.second)); + + return machineAccumulator; + } + + /** + * \return The tick on which the experiment should start using the topology of this section. + */ + uint32_t getStartTick() const + { + return startTick; + } + + private: + /** + * \brief The datacenter that is used for experiments. + */ + DatacenterType datacenter; + + /** + * \brief The tick when the next sections starts. This is -1 if this is the last section. + */ + uint32_t startTick; + }; + +} diff --git a/Simulator/include/simulation/history/History.h b/Simulator/include/simulation/history/History.h new file mode 100644 index 00000000..f1d826eb --- /dev/null +++ b/Simulator/include/simulation/history/History.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +namespace Simulation { + template + class History { + public: + void addSnapshotAtTick(uint32_t tick, Type snapshot) + { + history.insert(std::make_pair(tick, snapshot)); + } + + const auto& snapshotsAtTick(uint32_t tick) + { + return history.equal_range(tick); + } + + typename std::unordered_map::const_iterator begin() + { + return history.begin(); + } + + typename std::unordered_map::const_iterator end() + { + return history.end(); + } + + void clear() + { + history.clear(); + } + + size_t size() + { + return history.size(); + } + + private: + // Maps ticks to histories of workloads + std::unordered_multimap history; + }; +} \ No newline at end of file diff --git a/Simulator/include/simulation/history/MachineSnapshot.h b/Simulator/include/simulation/history/MachineSnapshot.h new file mode 100644 index 00000000..49ce1313 --- /dev/null +++ b/Simulator/include/simulation/history/MachineSnapshot.h @@ -0,0 +1,17 @@ +#pragma once + +namespace Simulation { + /* + POD class that represents the state of a machine. + */ + class MachineSnapshot { + public: + MachineSnapshot(int id, int currentWorkload, float temp, float load, uint32_t mem) : id(id), currentWorkload(currentWorkload), temperature(temp), loadFraction(load), usedMemory(mem) {} + + int id; + int currentWorkload; + float temperature; + float loadFraction; + uint32_t usedMemory; + }; +} \ No newline at end of file diff --git a/Simulator/include/simulation/history/SimulationHistory.h b/Simulator/include/simulation/history/SimulationHistory.h new file mode 100644 index 00000000..f43968b7 --- /dev/null +++ b/Simulator/include/simulation/history/SimulationHistory.h @@ -0,0 +1,81 @@ +#pragma once +#include "History.h" +#include "WorkloadSnapshot.h" +#include "MachineSnapshot.h" + +#include + +namespace Simulation +{ + using WorkloadHistory = History; + using MachineHistory = History; + using HistoryRef = std::tuple, std::reference_wrapper>; + + class SimulationHistory + { + public: + /* + Adds the workload snapshot at the given tick. + */ + void addSnapshot(uint32_t tick, WorkloadSnapshot snapshots) + { + workloadHistory.addSnapshotAtTick(tick, snapshots); + } + + /* + Adds the machine snapshot at the given tick. + */ + void addSnapshot(uint32_t tick, MachineSnapshot snapshots) + { + machineHistory.addSnapshotAtTick(tick, snapshots); + } + + /* + Returns the equal_range of the workload snapshots at the given tick. + */ + auto getWorkloadSnapshot(uint32_t tick) + { + return workloadHistory.snapshotsAtTick(tick); + } + + /* + Returns the equal_range of the machine snapshots at the given tick. + */ + auto getMachineSnapshot(uint32_t tick) + { + return machineHistory.snapshotsAtTick(tick); + } + + /* + Returns a const tuple ref of the entire cached history of machines and workloads. + */ + const HistoryRef getHistory() + { + return std::make_tuple( + std::ref(workloadHistory), + std::ref(machineHistory) + ); + } + + /* + Clears the cache of history. + */ + void clearHistory() + { + workloadHistory.clear(); + machineHistory.clear(); + } + + /* + Returns the number of snapshots that are in the history cache. + */ + size_t historySize() + { + return workloadHistory.size(); + } + + private: + WorkloadHistory workloadHistory; + MachineHistory machineHistory; + }; +} \ No newline at end of file diff --git a/Simulator/include/simulation/history/WorkloadSnapshot.h b/Simulator/include/simulation/history/WorkloadSnapshot.h new file mode 100644 index 00000000..1e7e2695 --- /dev/null +++ b/Simulator/include/simulation/history/WorkloadSnapshot.h @@ -0,0 +1,13 @@ +#pragma once + +namespace Simulation +{ + class WorkloadSnapshot + { + public: + WorkloadSnapshot(uint32_t id, uint32_t flopsDone) : flopsDone(flopsDone), id(id) {} + + uint32_t flopsDone; + uint32_t id; + }; +} \ No newline at end of file diff --git a/Simulator/include/simulation/schedulers/FirstInFirstOutScheduler.h b/Simulator/include/simulation/schedulers/FirstInFirstOutScheduler.h new file mode 100644 index 00000000..0cac0dfa --- /dev/null +++ b/Simulator/include/simulation/schedulers/FirstInFirstOutScheduler.h @@ -0,0 +1,37 @@ +#pragma once +#include "Scheduler.h" +#include + +namespace Simulation +{ + class FirstInFirstOutScheduler : public Scheduler + { + protected: + ~FirstInFirstOutScheduler() + { + } + + public: + /* + Distribute workloads according to the FIFO principle + */ + void schedule(std::vector>& machines, std::vector workloads) override + { + if (workloads.size() == 0) + return; + + // Find the first workload with dependencies finished + int index = 0; + while(!workloads.at(index)->dependencyFinished) + index = (++index) % workloads.size(); + + std::for_each( + machines.begin(), + machines.end(), + [index, &workloads](std::reference_wrapper& machine) { + machine.get().giveTask(workloads.at(index)); + } + ); + } + }; +} diff --git a/Simulator/include/simulation/schedulers/Scheduler.h b/Simulator/include/simulation/schedulers/Scheduler.h new file mode 100644 index 00000000..b084e8f6 --- /dev/null +++ b/Simulator/include/simulation/schedulers/Scheduler.h @@ -0,0 +1,25 @@ +#pragma once +#include "simulation/workloads/Workload.h" +#include "modeling/machine/Machine.h" + +#include + +namespace Simulation +{ + /* + Provides a strategy for load balancing. + */ + class Scheduler + { + public: + virtual ~Scheduler() + { + + } + + /* + Divides the workloads over the given machines. + */ + virtual void schedule(std::vector>& machines, std::vector workloads) = 0; + }; +} diff --git a/Simulator/include/simulation/schedulers/ShortestRemainingTimeScheduler.h b/Simulator/include/simulation/schedulers/ShortestRemainingTimeScheduler.h new file mode 100644 index 00000000..15265985 --- /dev/null +++ b/Simulator/include/simulation/schedulers/ShortestRemainingTimeScheduler.h @@ -0,0 +1,46 @@ +#pragma once +#include "Scheduler.h" +#include + +namespace Simulation +{ + class ShortestRemainingTimeScheduler : public Scheduler + { + protected: + ~ShortestRemainingTimeScheduler() + { + } + + public: + /* + Distribute workloads according to the srtf principle + */ + void schedule(std::vector>& machines, std::vector workloads) override + { + if (workloads.size() == 0) + return; + + std::sort( + workloads.begin(), + workloads.end(), + [](Workload* a, Workload* b) -> bool { + return a->getRemainingOperations() < b->getRemainingOperations(); + } + ); + + int taskIndex = 0; + + std::for_each( + machines.begin(), + machines.end(), + [&workloads, &taskIndex](Modeling::Machine& machine) { + while (!workloads.at(taskIndex)->dependencyFinished) + taskIndex = (++taskIndex) % workloads.size(); + + machine.giveTask(workloads.at(taskIndex)); + taskIndex = (++taskIndex) % workloads.size(); + } + ); + } + }; +} diff --git a/Simulator/include/simulation/workloads/Workload.h b/Simulator/include/simulation/workloads/Workload.h new file mode 100644 index 00000000..9de57990 --- /dev/null +++ b/Simulator/include/simulation/workloads/Workload.h @@ -0,0 +1,80 @@ +#pragma once +#include + +namespace Simulation +{ + /* + The Workload class models a workload. + */ + class Workload + { + public: + /* + Initializes the TOTAL_FLOPS and the remainingFlops to the size. + */ + Workload(int size, int startTick, int dbId, int traceId, int dependency); + + /* + Decreases the remainingFlops by the given amount. + */ + void doOperations(uint32_t opCount); + + /* + Returns the amount of operations left to do. + */ + uint32_t getRemainingOperations() const; + + /* + Returns the total amount of operations, including finished ones. + */ + uint32_t getTotalOperations() const; + + /* + Returns true if the workload has been finished. + */ + bool isFinished() const; + + /* + Returns the id of this workload. + */ + uint32_t getId() const; + + /* + Returns the dependency id of this workload. + */ + int getDependencyId() const; + + /** + * \return The start tick of this workload. + */ + uint32_t getStartTick() const + { + return START_TICK; + } + + // True if the dependency of this workload has finished. + bool dependencyFinished = false; + + private: + // The id of the workload this workload depends on. + int dependencyId; + + // Finished is true if the remainingFlops is 0. + bool finished = false; + + // The amount of operations done. + uint32_t remainingFlops; + + // The total amount of operations required to finish this task. + uint32_t TOTAL_FLOPS; + + // The tick during which this workload was started. + uint32_t START_TICK; + + // The id of this workload in the database + uint32_t ID; + + // The id of the trace this workload belongs to in the database. + uint32_t TRACE_ID; + }; +} diff --git a/Simulator/include/simulation/workloads/WorkloadPool.h b/Simulator/include/simulation/workloads/WorkloadPool.h new file mode 100644 index 00000000..28a2ad04 --- /dev/null +++ b/Simulator/include/simulation/workloads/WorkloadPool.h @@ -0,0 +1,43 @@ +#pragma once +#include "simulation/workloads/Workload.h" +#include + +namespace Simulation +{ + class WorkloadPool + { + public: + /* + Adds the given workload to this pool of workloads. + */ + void addWorkload(Workload w); + + /* + Returns a reference to the vector of workloads. + */ + std::vector getWorkloads(uint32_t currentTick); + + /* + Returns a reference to the workload with the given id. + */ + Workload& getWorkload(int id); + + /* + Removes all workloads that are finished. + */ + void clearFinishedWorkloads(); + + /* + Returns true if the workloads vector of this pool is empty. + */ + bool isEmpty(); + + private: + /* + Sets all dependencyFinished to true of workloads with the given id as dependency. + */ + void setDependenciesFinished(int id); + + std::vector workloads; + }; +} -- cgit v1.2.3