diff options
Diffstat (limited to 'Simulator/include/database')
| -rw-r--r-- | Simulator/include/database/Database.h | 74 | ||||
| -rw-r--r-- | Simulator/include/database/Queries.h | 131 | ||||
| -rw-r--r-- | Simulator/include/database/Query.h | 124 | ||||
| -rw-r--r-- | Simulator/include/database/QueryExecuter.h | 155 | ||||
| -rw-r--r-- | Simulator/include/database/QueryResult.h | 25 |
5 files changed, 509 insertions, 0 deletions
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 <sqlite3.h> +#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<int> 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<int, int, int, int, std::string, std::string> 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<int, int, std::string, std::string> GET_PATH_BY_ID(std::string(R"query( + SELECT id, simulation_id, name, datetime_created FROM paths WHERE id = $id; + )query")); + + Query<int, int, int, int> 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: <std::string : scheduler_name> + Binds: <int : id> + */ + Query<std::string> 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: <int : trace_id> + Binds: <int : id> + */ + Query<int> 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: <int : id, std::string : name, int : datacenter_id, std::string : type> + Binds: <int : datacenter_id> + */ + Query<int, std::string, int, std::string> 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: <int : id, std::string : name, int : capacity> + Binds: <int : tile.room_id> + */ + Query<int, std::string, int> 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: <int : id, int : position> + Binds: <int : rack_id> + */ + Query<int, int> 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: <int : id, int : start_tick, inn : total_flop_count, int : trace_id, int : task_dependency_id> + Binds: <int : trace_id> + */ + Query<int, int, int, int, int> 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: <int : slot, int : machine_speed, int : cores, int : energy_consumption, int : failure_model_id> + Binds: <int : machine.rack_id> + */ + Query<int, int, int, int, int> 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: <int : slot, int : machine_speed, int : cores, int : energy_consumption, int : failure_model_id> + Binds: <int : machine.rack_id> + */ + Query<int, int, int, int, int> 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: <int : task_id, int : experiment_id, int : tick, int : flops_left> + */ + 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: <int : task_id, int : machine_id, int : experiment_id, int : tick, float : temperature_c, int : in_use_memory_mb, float : load_fraction> + */ + 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 <assert.h> +#include <sqlite3.h> + +namespace Database +{ + + namespace Specializations + { + template<typename ReturnType> + ReturnType getResult(int location, sqlite3_stmt* statement); + + template<> + inline int getResult<int>(int location, sqlite3_stmt* statement) + { + return sqlite3_column_int(statement, location); + } + + template<> + inline std::string getResult<std::string>(int location, sqlite3_stmt* statement) + { + return std::string(reinterpret_cast<const char*>(sqlite3_column_text(statement, location))); + } + + template<typename ValueType> + void bind(ValueType value, int location, sqlite3_stmt* statement); + + template<> + inline void bind<int>(int value, int location, sqlite3_stmt* statement) + { + int rc = sqlite3_bind_int(statement, location, value); + assert(rc == SQLITE_OK); + } + + template<> + inline void bind<float>(float value, int location, sqlite3_stmt* statement) + { + int rc = sqlite3_bind_double(statement, location, static_cast<double>(value)); + assert(rc == SQLITE_OK); + } + } + + + + template<typename ...ReturnTypes> + 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<int>(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<typename ValueType> + void bind(ValueType value, int location) + { + Specializations::bind<ValueType>(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<typename ReturnType> + ReturnType getResult(int location) + { + return Specializations::getResult<ReturnType>(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 <tuple> +#include <sqlite3.h> +#include <memory> +#include <vector> + +namespace Database +{ + template<typename ...ReturnTypes> + 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<ReturnTypes...>& setQuery(Query<ReturnTypes...> query) + { + this->query = std::make_unique<Query<ReturnTypes...>>(query); + this->query->prepare(database); + return *this; + } + + /* + Binds the given name-value pairs to the statement. + Recursive case. + */ + template<typename BindType> + QueryExecuter<ReturnTypes...>& bindParams(BindType locationValuePair, int depth = 1) + { + query->template bind<BindType>(locationValuePair, depth); + return *this; + } + + /* + Binds the given name-value pairs to the statement. + Recursive case. + */ + template<typename BindType, typename ...BindTypes> + QueryExecuter<ReturnTypes...>& bindParams(BindType locationValuePair, BindTypes... locationValuePairs, int depth = 1) + { + query->template bind<BindType>(locationValuePair, depth); + bindParams<BindTypes...>(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<ReturnTypes...> executeOnce() + { + query->step(); + return QueryResult<ReturnTypes...>(getResult<ReturnTypes...>()); + } + + /* + Steps the query. + */ + bool step() + { + return query->step(); + } + + /* + Returns the result. + */ + QueryResult<ReturnTypes...> result() + { + return QueryResult<ReturnTypes...>(getResult<ReturnTypes...>()); + } + + /* + Executes the query and returns a vector of tuples for each result. + */ + std::vector<QueryResult<ReturnTypes...>> execute(int limit = 0) + { + std::vector<QueryResult<ReturnTypes...>> 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<ReturnTypes...> row = getResult<ReturnTypes...>(); + result.emplace_back(row); + + } while (more); + + return result; + } + + /* + Resets the sqlite3 query object. + */ + QueryExecuter<ReturnTypes...>& reset() + { + query->reset(); + return *this; + } + + private: + /* + Returns the results after executing the query. + Base case. + */ + template<typename ReturnType> + std::tuple<ReturnType> getResult(int depth = 0) + { + return std::tuple<ReturnType>(query->template getResult<ReturnType>(depth)); + } + + /* + Returns the results after executing the query. + Recursive Case. + */ + template<typename FirstReturnType, typename SecondReturnType, typename ...OtherReturnTypes> + std::tuple<FirstReturnType, SecondReturnType, OtherReturnTypes...> getResult(int depth = 0) + { + std::tuple<FirstReturnType> first = std::tuple<FirstReturnType>(query->template getResult<FirstReturnType>(depth)); + std::tuple<SecondReturnType, OtherReturnTypes...> rest = getResult<SecondReturnType, OtherReturnTypes...>(depth + 1); + return std::tuple_cat(first, rest); + } + + /* + Returns an empty tuple for when there are no return values. + */ + template<typename ...NoTypes> + std::tuple<> getResult(int depth = 0) const + { + return std::tuple<>(); + } + + std::unique_ptr<Query<ReturnTypes...>> 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 <tuple> + +namespace Database +{ + template<class ...ReturnTypes> + class QueryResult + { + public: + explicit QueryResult(ReturnTypes... returnValues) : values(returnValues...) {} + explicit QueryResult(std::tuple<ReturnTypes...> 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<class ReturnType, int Index> + ReturnType get() + { + return std::get<Index>(values); + } + + std::tuple<ReturnTypes...> values; + }; +} |
