// etabench // Copyright (C) 2022 xaizek <xaizek@posteo.net> // // This file is part of etabench. // // etabench is free software: you can redistribute it and/or modify // it under the terms of version 3 of the GNU General Public License // as published by the Free Software Foundation. // // etabench is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with etabench. If not, see <https://www.gnu.org/licenses/>. #include <cmath> #include <algorithm> #include <filesystem> #include <iterator> #include <memory> #include <ranges> #include <stdexcept> #include <string> #include <utility> #include <vector> #include <fmt/core.h> #include "Args.hpp" #include "EtaBench.hpp" #include "Report.hpp" #include "algs.hpp" #include "core.hpp" #include "profiles.hpp" template <typename T, typename... Args> inline std::vector<std::unique_ptr<T>> make_unique_vector(Args &&...args) { std::vector<std::unique_ptr<T>> v; (v.emplace_back(std::move(args)), ...); return v; } static std::vector<int> parseList(const std::string &list, int max, const std::vector<int> &def) { if (list == "*") { auto iota = std::views::iota(0, max); return std::vector<int>(iota.begin(), iota.end()); } std::vector<int> result; for (const auto item : std::views::split(list, ',')) { auto b = item.begin(); auto e = std::ranges::next(b, item.end()); std::string part(b, e); std::size_t end; int num = std::stoi(part, &end); if (end != part.size()) { throw std::runtime_error("Bad list item: " + part); } if (num <= 0 || num > max) { throw std::runtime_error("Out of range: " + part); } result.push_back(num - 1); } if (result.empty()) { result = def; } std::ranges::sort(result, std::less<>()); auto dups = std::ranges::unique(result); result.erase(dups.begin(), dups.end()); return result; } int main(int argc, char *argv[]) { try { Args args(argc, argv); int total = args.getTotal(); auto algs = make_unique_vector<EtaAlg>( std::make_unique<AccelerationAlg>(), std::make_unique<AverageAlg>(), std::make_unique<FirefoxAlg>(), std::make_unique<GravityAlg>(), std::make_unique<ImmediateAlg>(), std::make_unique<LookBackAlg>(20), std::make_unique<SmoothingAlg>(10), std::make_unique<SwitchAlg>(), std::make_unique<WindowAlg>(20, 1.0f) ); auto disabledAlgs = make_unique_vector<EtaAlg>( std::make_unique<AveChangeLimitAlg>(), std::make_unique<CombinedAlg>(make_unique_vector<EtaAlg>( std::make_unique<AverageAlg>(), std::make_unique<ImmediateAlg>() )), std::make_unique<ExponentialAlg>(1), std::make_unique<ImmChangeLimitAlg>(), std::make_unique<SlownessAlg>(1) ); auto profs = make_unique_vector<Profile>( std::make_unique<ConstantProfile>(10), std::make_unique<LinearProfile>(total, -15), std::make_unique<LinearProfile>(total, -30), std::make_unique<LinearProfile>(total, -45), std::make_unique<LinearProfile>(total, -60), std::make_unique<LinearProfile>(total, -75), std::make_unique<LinearProfile>(total, 15), std::make_unique<LinearProfile>(total, 30), std::make_unique<LinearProfile>(total, 45), std::make_unique<LinearProfile>(total, 60), std::make_unique<LinearProfile>(total, 75), std::make_unique<RandomProfile>(10, 20), std::make_unique<ReplayProfile>(), std::make_unique<SawProfile>(20), std::make_unique<SquareProfile>(20), std::make_unique<StepProfile>(10) ); auto algsIota = std::views::iota(0, (int)algs.size()); std::vector<int> defAlgs(algsIota.begin(), algsIota.end()); algs.insert(algs.cend(), std::make_move_iterator(disabledAlgs.begin()), std::make_move_iterator(disabledAlgs.end())); auto profsIota = std::views::iota(0, (int)profs.size()); std::vector<int> defProfs(profsIota.begin(), profsIota.end()); if (args.shouldListAlgs()) { fmt::print("Algorithms:\n"); for (int i = 0; const auto &alg : algs) { fmt::print("{:2}. {}\n", ++i, alg->getName()); } return 0; } if (args.shouldListProfs()) { fmt::print("Profiles:\n"); for (int i = 0; const auto &prof : profs) { fmt::print("{:2}. {}\n", ++i, prof->getName()); } return 0; } // Doing the check here to abort on incorrect information before doing // any work. std::string plotDir = args.getPlotDir(); std::string montageFile = args.getMontageFile(); if (std::filesystem::exists(plotDir)) { fmt::print("Plotting directory '{}' already exists\n", plotDir); return 1; } EtaBench bench; for (auto &profIdx : parseList(args.getProfs(), profs.size(), defProfs)) { bench.addProfile(std::move(profs[profIdx])); } for (auto &algIdx : parseList(args.getAlgs(), algs.size(), defAlgs)) { bench.addAlg(std::move(algs[algIdx])); } Report report; bench.run(total, report); report.print(args.isVerbose()); if (!plotDir.empty() || !montageFile.empty()) { report.plot(plotDir, montageFile); } } catch (const std::exception &e) { fmt::print("Error: {}\n", e.what()); return 1; } catch (...) { fmt::print("Unknown exception\n"); return 1; } return 0; }