diff --git a/CMakeLists.txt b/CMakeLists.txt index ce0c0f0..814f216 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size -Wl,0x1000 # otool -lV build/ml | grep stack # set(CMAKE_CXX_FLAGS "-Wall -Wextra") -# set(CMAKE_CXX_FLAGS_RELEASE "-O3") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") include_directories(/usr/local/opt/openssl/include ${CMAKE_SOURCE_DIR}/clib ${CMAKE_SOURCE_DIR} ) @@ -30,6 +30,7 @@ set(SOURCE ml_date.cpp ml_string.cpp ml_util.cpp + ml_profiler.cpp clib/csvparser.cpp clib/sslclient.cpp clib/json11.cpp diff --git a/ml.cpp b/ml.cpp index 5e4a90c..030fec8 100644 --- a/ml.cpp +++ b/ml.cpp @@ -4,6 +4,7 @@ #include "ml_date.h" #include "ml_string.h" #include "ml_util.h" +#include "ml_profiler.h" #include "clib/csvparser.h" #include "clib/sslclient.h" @@ -710,6 +711,8 @@ MlValue MlValue::eval(MlEnvironment &env) { for (size_t i = 0; i < args.size(); i++) args[i] = args[i].eval(env); + MlPerfMon::instance().add_method_call(function.type == LAMBDA ? "lambda" : function.str); + return function.apply( args, env @@ -1651,7 +1654,7 @@ namespace builtin { high_resolution_clock::time_point t2 = high_resolution_clock::now(); duration time_span = t2 - t1; - std::cout << args[0].as_string() << " " << time_span.count() << " ms" << std::endl; + std::cerr << args[0].as_string() << " " << time_span.count() << " ms" << std::endl; return acc; } @@ -1718,20 +1721,15 @@ bool MlEnvironment::has(std::string name) const { // Get the value associated with this name in this scope MlValue MlEnvironment::get(const std::string &name) const { - // Meta operations - if (name == "eval") return MlValue("eval", builtin::eval); - if (name == "type") return MlValue("type", builtin::get_type_name); - if (name == "parse") return MlValue("parse", builtin::parse); - // Special forms - if (name == "do") return MlValue("do", builtin::do_block); if (name == "if") return MlValue("if", builtin::if_then_else); + if (name == "define") return MlValue("define", builtin::define); + if (name == "do") return MlValue("do", builtin::do_block); if (name == "for") return MlValue("for", builtin::for_loop); if (name == "while") return MlValue("while", builtin::while_loop); if (name == "scope") return MlValue("scope", builtin::scope); if (name == "quote") return MlValue("quote", builtin::quote); if (name == "defun") return MlValue("defun", builtin::defun); - if (name == "define") return MlValue("define", builtin::define); if (name == "lambda") return MlValue("lambda", builtin::lambda); if (name == "benchmark") return MlValue("benchmark", builtin::benchmark); @@ -1743,6 +1741,11 @@ MlValue MlEnvironment::get(const std::string &name) const { if (name == ">=") return MlValue(">=", builtin::greater_eq); if (name == "<=") return MlValue("<=", builtin::less_eq); + // Meta operations + if (name == "eval") return MlValue("eval", builtin::eval); + if (name == "type") return MlValue("type", builtin::get_type_name); + if (name == "parse") return MlValue("parse", builtin::parse); + // Arithmetic operations if (name == "+") return MlValue("+", builtin::sum); if (name == "-") return MlValue("-", builtin::subtract); @@ -1862,7 +1865,7 @@ int main(int argc, char *argv[]) { } // help if (cmdOptionExists(argv, argv + argc, "-h")) { - std::cout << "Usage:\n\t-h print this help\n\t-f source_file - executes code in file\n\t-c code - runs passed code\n\t-i runs repl\n\t-b skip stdlib loading\n\t-v prints version string\n\n"; + std::cout << "Usage:\n\t-h print this help\n\t-f source_file - executes code in file\n\t-c code - runs passed code\n\t-i runs repl\n\t-b skip stdlib loading\n\t-p prints profile info at the end\n\t-v prints version string\n\n"; return 0; } // version @@ -1870,6 +1873,9 @@ int main(int argc, char *argv[]) { std::cout << VERSION << std::endl; return 0; } + // performance monitor on + if (cmdOptionExists(argv, argv + argc, "-p")) + MlPerfMon::instance().turnOn(); // passed code if (cmdOptionExists(argv, argv + argc, "-c")) { std::vector codes = getCmdOption(argv, argc, "-c"); @@ -1885,11 +1891,15 @@ int main(int argc, char *argv[]) { repl(env); } + MlPerfMon::instance().print_results(); + + return 0; + } catch (MlError &e) { std::cerr << e.description() << std::endl; } catch (std::runtime_error &e) { std::cerr << e.what() << std::endl; } - return 0; + return 1; } diff --git a/ml_profiler.cpp b/ml_profiler.cpp new file mode 100644 index 0000000..85fe5ac --- /dev/null +++ b/ml_profiler.cpp @@ -0,0 +1,44 @@ + +#include "ml_profiler.h" + +#include +#include + +void MlPerfMon::turnOn() { + perfOn = true; + +} + +void MlPerfMon::add_method_call(const std::string &method) { + if (perfOn) { + auto search = calls_counter.find(method); + if (search != calls_counter.end()) { + calls_counter[method] = search->second + 1; + } else { + calls_counter.insert({method, 1}); + } + } +} + +void MlPerfMon::print_results() { + if (perfOn) { + std::cerr << std::endl << std::endl << "Call Frequency" << std::endl; + + using callcount = std::pair; + + std::vector v(begin(calls_counter), end(calls_counter)); + std::sort(std::begin(v), std::end(v), [](const callcount& a, const callcount& b) { return a.second > b.second; }); + + int i {0}; + for(auto &p : v) { + // TODO when method name longer than 15 chars, here it crashes + p.first.insert(p.first.end(), 15 - p.first.size(), ' '); + std::cerr << p.first << " " << p.second << std::endl; + + i++; + if (i > 10) break; + } + + std::cerr << std::endl; + } +} diff --git a/ml_profiler.h b/ml_profiler.h new file mode 100644 index 0000000..505422a --- /dev/null +++ b/ml_profiler.h @@ -0,0 +1,28 @@ +#pragma once + +#include "ml.h" + +#include + +class MlPerfMon { + +private: + MlPerfMon() : perfOn(false) {}; + +public: + + // https://stackoverflow.com/questions/43523509/simple-singleton-example-in-c + static MlPerfMon& instance(){ + static MlPerfMon instance; + return instance; + } + + void turnOn(); + + void add_method_call(const std::string &method); + void print_results(); + +private: + bool perfOn; + std::unordered_map calls_counter; +};