From d77800b2f97d271c7fe9a49336f7547b018eae64 Mon Sep 17 00:00:00 2001 From: Marko Budiselic <mbudiselicbuda@gmail.com> Date: Sun, 21 Feb 2016 22:21:15 +0100 Subject: [PATCH] Basic CREATE query works (e.g. CREATE (n {age:25, name:"test"}) RETURN n) --- dc/dynamic_lib.hpp | 22 ++++--- query_engine/code_compiler.hpp | 12 ++-- query_engine/code_executor.hpp | 27 -------- query_engine/code_generator.hpp | 13 ---- query_engine/compile.sh | 1 + query_engine/debug.hpp | 23 +++++++ query_engine/i_code_cpu.hpp | 3 +- query_engine/main.cpp | 14 +++- query_engine/program_executor.hpp | 33 ++++++++++ .../{code_loader.hpp => program_loader.hpp} | 31 ++++----- query_engine/query_engine.hpp | 20 +++--- query_engine/query_program.hpp | 17 +++++ query_engine/query_result.hpp | 35 ++++++++-- query_engine/query_stripped.hpp | 20 ++++++ query_engine/query_stripper.hpp | 66 +++++++++++++++---- query_engine/template/template_code_cpu.cpp | 3 +- query_engine/template/template_code_cpu.hpp | 9 --- query_engine/traverser/create_traverser.hpp | 40 +++++++---- query_engine/traverser/node_traverser.hpp | 49 -------------- query_engine/util.hpp | 8 +++ storage/edge.hpp | 2 +- storage/model/properties/properties.hpp | 6 +- storage/record_accessor.hpp | 5 ++ storage/vertex.hpp | 2 +- utils/time/timer.hpp | 19 ++++++ 25 files changed, 302 insertions(+), 178 deletions(-) delete mode 100644 query_engine/code_executor.hpp create mode 100644 query_engine/debug.hpp create mode 100644 query_engine/program_executor.hpp rename query_engine/{code_loader.hpp => program_loader.hpp} (75%) create mode 100644 query_engine/query_program.hpp create mode 100644 query_engine/query_stripped.hpp delete mode 100644 query_engine/template/template_code_cpu.hpp delete mode 100644 query_engine/traverser/node_traverser.hpp create mode 100644 query_engine/util.hpp create mode 100644 utils/time/timer.hpp diff --git a/dc/dynamic_lib.hpp b/dc/dynamic_lib.hpp index c63b3f64f..7b99b3938 100644 --- a/dc/dynamic_lib.hpp +++ b/dc/dynamic_lib.hpp @@ -3,6 +3,11 @@ #include <string> #include <stdexcept> #include <dlfcn.h> +#include <atomic> + +#include <iostream> +using std::cout; +using std::endl; template<typename T> class DynamicLib @@ -10,26 +15,25 @@ class DynamicLib private: using produce_t = typename T::produce; using destruct_t = typename T::destruct; + std::atomic<uint8_t> counter; public: produce_t produce_method; destruct_t destruct_method; DynamicLib(const std::string& lib_path) : - lib_path(lib_path) + lib_path(lib_path), + lib_object(nullptr) { + load(); } - // TODO debug why this doesn't work typename T::lib_object* instance() { - // TODO singleton, lazy, concurrency - // ifs are uncommented -> SEGMENTATION FAULT - // TODO debug - // if (dynamic_lib == nullptr) - this->load(); - // if (dynamic_lib == nullptr) + // TODO singleton, concurrency + if (lib_object == nullptr) { lib_object = this->produce_method(); + } return lib_object; } @@ -50,7 +54,7 @@ public: private: std::string lib_path; void *dynamic_lib; - typename T::lib_object* lib_object; + typename T::lib_object *lib_object; void load_lib() { diff --git a/query_engine/code_compiler.hpp b/query_engine/code_compiler.hpp index a2491bdbc..27a4e8c78 100644 --- a/query_engine/code_compiler.hpp +++ b/query_engine/code_compiler.hpp @@ -7,15 +7,17 @@ class CodeCompiler { public: + void compile(const std::string& in_file, const std::string& out_file) { auto compile_command = utils::prints( "clang++", - "-std=c++1y", - in_file, - "-o", out_file, - "-I../", - "-shared -fPIC" + // "-std=c++1y -O2 -DNDEBUG", // compile flags + "-std=c++1y", // compile flags + in_file, // input file + "-o", out_file, // ouput file + "-I../", // include paths + "-shared -fPIC" // shared library flags ); // synchronous call diff --git a/query_engine/code_executor.hpp b/query_engine/code_executor.hpp deleted file mode 100644 index 592e8197e..000000000 --- a/query_engine/code_executor.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include <string> - -#include "i_code_cpu.hpp" -#include "database/db.hpp" -#include "utils/log/logger.hpp" - -// preparations before execution -// execution -// postprocess the results - -using std::cout; -using std::endl; - -class CodeExecutor -{ -public: - void execute(ICodeCPU *code_cpu) - { - auto result = code_cpu->run(db); - cout << result->result << endl; - } - -private: - Db db; -}; diff --git a/query_engine/code_generator.hpp b/query_engine/code_generator.hpp index 9badb469c..d196e7b8c 100644 --- a/query_engine/code_generator.hpp +++ b/query_engine/code_generator.hpp @@ -10,19 +10,6 @@ using std::string; class CodeGenerator { public: - void generate_hpp(const uint64_t stripped_hash, const std::string& path) - { - string template_path = CONFIG(config::TEMPLATE_CPU_HPP_PATH); - string template_file = utils::read_file(template_path.c_str()); - string generated = template_engine.render( - template_file, - { - {"stripped_hash", std::to_string(stripped_hash)}, - {"data_type", "int"} - } - ); - utils::write_file(generated, path); - } void generate_cpp(const std::string& query, const uint64_t stripped_hash, diff --git a/query_engine/compile.sh b/query_engine/compile.sh index e17021c50..21a2d6860 100755 --- a/query_engine/compile.sh +++ b/query_engine/compile.sh @@ -5,4 +5,5 @@ # clang++ -std=c++1y create_return.cpp -o create_return.so -I../../../ -shared -fPIC # cd ../.. # clang++ -std=c++1y -g -I../ -I ../lib/yaml-cpp/include main.cpp ../cypher/cypher.cpp -o engine -L ../lib/yaml-cpp/build -l yaml-cpp -l dl +# clang++ -std=c++1y -g -O2 -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread clang++ -std=c++1y -g -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread diff --git a/query_engine/debug.hpp b/query_engine/debug.hpp new file mode 100644 index 000000000..87209a9db --- /dev/null +++ b/query_engine/debug.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <iostream> + +#include "storage/model/properties/traversers/jsonwriter.hpp" +#include "storage/model/properties/properties.hpp" + +using std::cout; +using std::endl; + +void print_props(const Properties& properties) +{ + StringBuffer buffer; + JsonWriter<StringBuffer> writer(buffer); + properties.accept(writer); + cout << buffer.str() << endl; +} + +#ifdef DEBUG +# define PRINT_PROPS(_PROPS_) print_props(_PROPS_); +#else +# define PRINT_PROPS(_) +#endif diff --git a/query_engine/i_code_cpu.hpp b/query_engine/i_code_cpu.hpp index 11bf85b9f..585f98404 100644 --- a/query_engine/i_code_cpu.hpp +++ b/query_engine/i_code_cpu.hpp @@ -1,12 +1,13 @@ #pragma once #include "query_engine/query_result.hpp" +#include "query_stripped.hpp" #include "database/db.hpp" class ICodeCPU { public: - virtual QueryResult::sptr run(Db& db) = 0; + virtual QueryResult::sptr run(Db& db, code_args_t& args) = 0; virtual ~ICodeCPU() {} }; diff --git a/query_engine/main.cpp b/query_engine/main.cpp index c44f426b7..2708ad900 100644 --- a/query_engine/main.cpp +++ b/query_engine/main.cpp @@ -3,6 +3,7 @@ #include "utils/command_line/arguments.hpp" #include "cypher/common.hpp" #include "query_engine.hpp" +#include "utils/time/timer.hpp" using std::cout; using std::endl; @@ -17,7 +18,18 @@ int main(int argc, char** argv) cout << "QUERY: " << cypher_query << endl; QueryEngine engine; - engine.execute(cypher_query); + // engine.execute(cypher_query); + + using std::placeholders::_1; + auto f = std::bind(&QueryEngine::execute, &engine, _1); + + cout << std::fixed << timer(f, cypher_query) << endl; + + // double counter = 0; + // for (int i = 0; i < 1000000; ++i) { + // counter += timer(f, cypher_query); + // } + // cout << 1000000 / (counter / 1000000000) << "create_transactions per sec" << endl; return 0; } diff --git a/query_engine/program_executor.hpp b/query_engine/program_executor.hpp new file mode 100644 index 000000000..1085a4078 --- /dev/null +++ b/query_engine/program_executor.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <string> + +#include "query_program.hpp" +#include "database/db.hpp" +#include "utils/log/logger.hpp" + +// preparations before execution +// execution +// postprocess the results + +#define DEBUG 1 +#include "query_engine/debug.hpp" + +using std::cout; +using std::endl; + +class ProgramExecutor +{ +public: + + auto execute(QueryProgram& program) + { + auto result = program.code->run(db, program.stripped.arguments); + print_props(*result->data["n"]->data[0]); + return result; + } + +public: + + Db db; +}; diff --git a/query_engine/code_loader.hpp b/query_engine/program_loader.hpp similarity index 75% rename from query_engine/code_loader.hpp rename to query_engine/program_loader.hpp index bb0be8346..ea924418a 100644 --- a/query_engine/code_loader.hpp +++ b/query_engine/program_loader.hpp @@ -4,7 +4,7 @@ #include <unordered_map> #include <memory> -// #define NOT_LOG_INFO +#define NOT_LOG_INFO #include "memgraph_dynamic_lib.hpp" #include "query_stripper.hpp" @@ -13,28 +13,27 @@ #include "utils/hashing/fnv.hpp" #include "config/config.hpp" #include "utils/log/logger.hpp" +#include "query_program.hpp" using std::string; using std::cout; using std::endl; - -class CodeLoader +class ProgramLoader { public: using sptr_code_lib = std::shared_ptr<CodeLib>; - CodeLoader() - : stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR)) - { - } + ProgramLoader() + : stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL)) {} - ICodeCPU* load_code_cpu(const string& query) + auto load(const string& query) { auto stripped = stripper.strip(query); - LOG_INFO("stripped_query=" + stripped); - auto stripped_hash = fnv(stripped); + LOG_INFO("stripped_query=" + stripped.query); + + auto stripped_hash = fnv(stripped.query); auto hash_string = std::to_string(stripped_hash); LOG_INFO("query_hash=" + hash_string); @@ -42,9 +41,10 @@ public: // code is already compiled and loaded, just return runnable // instance - if (code_lib_iter != code_libs.end()) - // TODO also return extracted arguments - return code_lib_iter->second->instance(); + if (code_lib_iter != code_libs.end()) { + auto code = code_lib_iter->second->instance(); + return QueryProgram(code, std::move(stripped)); + } // code has to be generated, compiled and loaded // TODO load output path from config @@ -61,12 +61,13 @@ public: code_libs.insert({{stripped_hash, code_lib}}); // return instance of runnable code (ICodeCPU) - return code_lib->instance(); + return QueryProgram(code_lib->instance(), std::move(stripped)); } private: + // TODO somehow remove int.. from here - QueryStripper<int, int, int> stripper; + QueryStripper<int, int, int, int> stripper; // TODO ifdef MEMGRAPH64 problem, how to use this kind // of ifdef functions? // uint64_t depends on fnv function diff --git a/query_engine/query_engine.hpp b/query_engine/query_engine.hpp index 086ac8702..3bb8bc84e 100644 --- a/query_engine/query_engine.hpp +++ b/query_engine/query_engine.hpp @@ -1,30 +1,26 @@ #pragma once -#include "code_loader.hpp" -#include "code_executor.hpp" +#include "program_loader.hpp" +#include "program_executor.hpp" #include "query_result.hpp" // // Current arhitecture: // query -> code_loader -> query_stripper -> [code_generator] // -> [code_compiler] -> code_executor -// class QueryEngine { public: - QueryEngine() - { - } - QueryResult* execute(const std::string& query) + auto execute(const std::string& query) { - executor.execute(loader.load_code_cpu(query)); - - throw std::runtime_error("implement me"); + auto program = program_loader.load(query); + auto result = program_executor.execute(program); + return result; } private: - CodeLoader loader; - CodeExecutor executor; + ProgramExecutor program_executor; + ProgramLoader program_loader; }; diff --git a/query_engine/query_program.hpp b/query_engine/query_program.hpp new file mode 100644 index 000000000..bc9079218 --- /dev/null +++ b/query_engine/query_program.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "i_code_cpu.hpp" +#include "query_stripped.hpp" + +struct QueryProgram +{ + QueryProgram(ICodeCPU* code, QueryStripped&& stripped) : + code(code), + stripped(std::forward<QueryStripped>(stripped)) {} + + QueryProgram(QueryProgram& other) = delete; + QueryProgram(QueryProgram&& other) = default; + + ICodeCPU *code; + QueryStripped stripped; +}; diff --git a/query_engine/query_result.hpp b/query_engine/query_result.hpp index c19dbb7b3..8c61ef8dd 100644 --- a/query_engine/query_result.hpp +++ b/query_engine/query_result.hpp @@ -1,15 +1,38 @@ #pragma once #include <string> +#include <vector> #include <memory> +#include <unordered_map> -class QueryResult +#include "storage/model/properties/properties.hpp" + +struct ResultList { -public: - using sptr = std::shared_ptr<QueryResult>; + using sptr = std::shared_ptr<ResultList>; + using data_t = std::vector<const Properties*>; - QueryResult(std::string result) : - result(result) {} + ResultList() = delete; + ResultList(ResultList& other) = delete; + ResultList(ResultList&& other) = default; - std::string result; + ResultList(data_t&& data) : + data(std::forward<data_t>(data)) {} + + std::vector<const Properties*> data; +}; + +struct QueryResult +{ + using sptr = std::shared_ptr<QueryResult>; + using data_t = std::unordered_map<std::string, ResultList::sptr>; + + QueryResult() = delete; + QueryResult(QueryResult& other) = delete; + QueryResult(QueryResult&& other) = default; + + QueryResult(data_t&& data) : + data(std::forward<data_t>(data)) {} + + data_t data; }; diff --git a/query_engine/query_stripped.hpp b/query_engine/query_stripped.hpp new file mode 100644 index 000000000..fe3bf5557 --- /dev/null +++ b/query_engine/query_stripped.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <vector> + +#include "storage/model/properties/property.hpp" + +using code_args_t = std::vector<Property::sptr>; + +struct QueryStripped +{ + QueryStripped(const std::string&& query, code_args_t&& arguments) : + query(std::forward<const std::string>(query)), + arguments(std::forward<code_args_t>(arguments)) {} + + QueryStripped(QueryStripped& other) = delete; + QueryStripped(QueryStripped&& other) = default; + + std::string query; + code_args_t arguments; +}; diff --git a/query_engine/query_stripper.hpp b/query_engine/query_stripper.hpp index 087ab2001..e1900b974 100644 --- a/query_engine/query_stripper.hpp +++ b/query_engine/query_stripper.hpp @@ -3,13 +3,22 @@ #include <string> #include <tuple> #include <utility> +#include <unordered_map> #include "cypher/cypher.h" #include "cypher/tokenizer/cypher_lexer.hpp" #include "utils/variadic/variadic.hpp" +#include "storage/model/properties/all.hpp" +#include "query_stripped.hpp" #include <iostream> +template<class T, class V> +void store_query_param(code_args_t& arguments, V&& v) +{ + arguments.emplace_back(std::make_shared<T>(std::forward<V>(v))); +} + template<typename ...Ts> class QueryStripper { @@ -17,32 +26,63 @@ public: QueryStripper(Ts&&... strip_types) : lexer(std::make_unique<CypherLexer>()), - strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)) - { - } + strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)) {} + QueryStripper(QueryStripper& other) = delete; + QueryStripper(QueryStripper&& other) : strip_types(std::move(other.strip_types)), - lexer(std::move(other.lexer)) - { - } + lexer(std::move(other.lexer)) {} - decltype(auto) strip(const std::string& query) + auto strip(const std::string& query) { - // TODO return hash and arguments + // TODO write this more optimal (resplace string + // concatenation with something smarter) + // TODO: in place substring replacement + auto tokenizer = lexer->tokenize(query); - std::string stripped = ""; - int counter = 0; + + // TMP size of supported token types constexpr auto size = std::tuple_size<decltype(strip_types)>::value; + + int counter = 0; + code_args_t stripped_arguments; + std::string stripped_query; + stripped_query.reserve(query.size()); + while (auto token = tokenizer.lookup()) { + // TODO: better implementation if (_or(token.id, strip_types, std::make_index_sequence<size>{})) { - stripped += "@" + std::to_string(counter++); + auto index = counter++; + switch (token.id) { + case TK_INT: + store_query_param<Int32>(stripped_arguments, + std::stoi(token.value)); + break; + case TK_STR: + store_query_param<String>(stripped_arguments, + token.value); + break; + case TK_BOOL: { + bool value = token.value[0] == 'T' || + token.value[0] == 't'; + store_query_param<Bool>(stripped_arguments, value); + break; + } + case TK_FLOAT: + store_query_param<Float>(stripped_arguments, + std::stof(token.value)); + break; + } + stripped_query += std::to_string(index); } else { - stripped += token.value; + stripped_query += token.value; } } - return stripped; + + return QueryStripped(std::move(stripped_query), + std::move(stripped_arguments)); } private: diff --git a/query_engine/template/template_code_cpu.cpp b/query_engine/template/template_code_cpu.cpp index 0d5001e44..b8159adea 100644 --- a/query_engine/template/template_code_cpu.cpp +++ b/query_engine/template/template_code_cpu.cpp @@ -2,6 +2,7 @@ #include <string> #include "query_engine/i_code_cpu.hpp" +#include "storage/model/properties/all.hpp" // TODO generate with the template engine // #include "storage/model/properties/jsonwriter.hpp" @@ -15,7 +16,7 @@ class {{class_name}} : public ICodeCPU { public: - QueryResult::sptr run(Db& db) override + QueryResult::sptr run(Db& db, code_args_t& args) override { {{code}} } diff --git a/query_engine/template/template_code_cpu.hpp b/query_engine/template/template_code_cpu.hpp deleted file mode 100644 index 2a823ccc0..000000000 --- a/query_engine/template/template_code_cpu.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "query_engine/i_code_cpu.hpp" - -class ICodeCPUData : ICodeCPU -{ -public: - {{data_type}} data; -}; diff --git a/query_engine/traverser/create_traverser.hpp b/query_engine/traverser/create_traverser.hpp index a1197cadb..f14128b39 100644 --- a/query_engine/traverser/create_traverser.hpp +++ b/query_engine/traverser/create_traverser.hpp @@ -4,38 +4,52 @@ #include <typeinfo> #include <map> -#include "storage/model/properties/properties.hpp" -#include "storage/model/properties/jsonwriter.hpp" #include "cypher/visitor/traverser.hpp" -#include "node_traverser.hpp" +#include "query_engine/util.hpp" using std::cout; using std::endl; class CreateTraverser : public Traverser { +private: + std::string key; + uint32_t index{0}; + public: std::string code; - Properties properties; void visit(ast::Create& create) override { - code = "\t\tauto& t = db.tx_engine.begin();\n"; - code += "\t\tauto vertex_accessor = db.graph.vertices.insert(t);\n"; + code += line("auto& t = db.tx_engine.begin();"); + code += line("auto vertex_accessor = db.graph.vertices.insert(t);"); + Traverser::visit(create); }; - void visit(ast::Node& node) override + void visit(ast::Property& property) override { - Traverser::visit(node); + key = property.idn->name; + + Traverser::visit(property); + + code += line("vertex_accessor.property("); + code += line("\t\"" + key + "\", args[" + std::to_string(index) + "]"); + code += line(");"); + + ++index; } void visit(ast::Return& ret) override { - code += "\t\tauto properties = vertex_accessor.properties();\n"; - code += "\t\tStringBuffer buffer;\n"; - code += "\t\tJsonWriter<StringBuffer> writer(buffer);\n"; - code += "\t\tproperties.accept(writer);\n"; - code += "\t\treturn std::make_shared<QueryResult>(buffer.str());\n"; + code += line("t.commit();"); + code += line("auto &properties = vertex_accessor.properties();"); + code += line("ResultList::data_t data = {&properties};"); + code += line("auto result_data = " + "std::make_shared<ResultList>(std::move(data));"); + code += line("QueryResult::data_t query_data = {{\"" + + ret.return_list->value->name + "\", result_data}};"); + code += line("return std::make_shared<QueryResult>" + "(std::move(query_data));"); } }; diff --git a/query_engine/traverser/node_traverser.hpp b/query_engine/traverser/node_traverser.hpp deleted file mode 100644 index 80942488f..000000000 --- a/query_engine/traverser/node_traverser.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "cypher/visitor/traverser.hpp" -#include "storage/model/properties/properties.hpp" - -struct NodeTraverser : public Traverser -{ - Properties traverse(ast::Node& node) - { - Traverser::visit(node); - return properties; - } - - void visit(ast::Property& property) override - { - key = property.idn->name; - Traverser::visit(property); - if (flag == Property::Flags::Int32) { - properties.set<Int32>(key, *int_value); - } - if (flag == Property::Flags::String) { - properties.set<String>(key, *string_value); - } - } - - void visit(ast::String& string) override - { - flag = Property::Flags::String; - string_value = std::make_shared<String>(string.value); - } - - void visit(ast::Integer& integer) override - { - flag = Property::Flags::Int32; - int_value = std::make_shared<Int32>(integer.value); - } - - void visit(ast::Node& node) override - { - Traverser::visit(node); - } - -private: - std::string key; - Property::Flags flag; - std::shared_ptr<Int32> int_value; - std::shared_ptr<String> string_value; - Properties properties; -}; diff --git a/query_engine/util.hpp b/query_engine/util.hpp new file mode 100644 index 000000000..74cac8f5b --- /dev/null +++ b/query_engine/util.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include <string> + +std::string line(std::string line) +{ + return "\t\t" + line + "\n"; +} diff --git a/storage/edge.hpp b/storage/edge.hpp index 5cc4e76d4..507af2925 100644 --- a/storage/edge.hpp +++ b/storage/edge.hpp @@ -2,7 +2,7 @@ #include "mvcc/record.hpp" #include "model/edge_model.hpp" -#include "model/properties/jsonwriter.hpp" +#include "model/properties/traversers/jsonwriter.hpp" class Edge : public mvcc::Record<Edge> { diff --git a/storage/model/properties/properties.hpp b/storage/model/properties/properties.hpp index 4452804c9..37a088ee4 100644 --- a/storage/model/properties/properties.hpp +++ b/storage/model/properties/properties.hpp @@ -7,9 +7,9 @@ class Properties { - using props_t = std::map<std::string, Property::sptr>; - public: + using sptr = std::shared_ptr<Properties>; + const Property& at(const std::string& key) const { auto it = props.find(key); @@ -56,6 +56,8 @@ public: } private: + using props_t = std::map<std::string, Property::sptr>; + props_t props; }; diff --git a/storage/record_accessor.hpp b/storage/record_accessor.hpp index 92b4cc3f4..b61bb8b34 100644 --- a/storage/record_accessor.hpp +++ b/storage/record_accessor.hpp @@ -60,6 +60,11 @@ public: record->data.props.template set<V>(key, std::forward<Args>(args)...); } + void property(const std::string& key, Property::sptr value) + { + record->data.props.set(key, std::move(value)); + } + Properties& properties() const { return record->data.props; diff --git a/storage/vertex.hpp b/storage/vertex.hpp index 4d534cd78..da4d571f8 100644 --- a/storage/vertex.hpp +++ b/storage/vertex.hpp @@ -2,7 +2,7 @@ #include "mvcc/record.hpp" #include "model/vertex_model.hpp" -#include "model/properties/jsonwriter.hpp" +#include "model/properties/traversers/jsonwriter.hpp" class Vertex : public mvcc::Record<Vertex> { diff --git a/utils/time/timer.hpp b/utils/time/timer.hpp new file mode 100644 index 000000000..28e337ac2 --- /dev/null +++ b/utils/time/timer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include <utility> +#include <chrono> + +using time_point_t = std::chrono::high_resolution_clock::time_point; + +#define duration(a) \ + std::chrono::duration_cast<std::chrono::nanoseconds>(a).count() + +#define time_now() std::chrono::high_resolution_clock::now() + +template<typename F, typename... Args> +double timer(F func, Args&&... args) +{ + time_point_t start_time = time_now(); + func(std::forward<Args>(args)...); + return duration(time_now() - start_time); +}