//ProjectEuler/ProjectEulerCPP/headers/benchmark.hpp //Matthew Ellison // Created: 07-08-20 //Modified: 08-28-20 //These are functions that help determine an average run time for the problems /* Copyright (C) 2020 Matthew Ellison This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef BENCHMARK_HPP #define BENCHMARK_HPP #include #include #include "ProblemSelection.hpp" void printBenchmarkMenu(); //Print the benchmark menu int getBenchmarkMenuSelection(); //Returns a valid menu option bool isValidBenchmarkMenu(int selection); //Determines if a value is a valid menu option. Helper for getBenchmarkMenuSelection void runSpecific(); //Determines which problem user wants to run and runs it void runAllShort(); //Runs all problems except a few that are specified because of run length void runAll(); //Runs all problems unsigned int getNumberOfTimesToRun(); //Asks how many times a problem is supposed to run and returns the value double runProblem(Problem* problem, unsigned int timesToRun); //Runs the problem the given number of times std::string getBenchmarkResults(Problem* problem, double totalTime, unsigned int timesRan); //Prints the benchmark results of a problem //Variables //Valid menu options enum BENCHMARK_OPTIONS {RUN_SPECIFIC = 1, RUN_ALL_SHORT, RUN_ALL, BENCHMARK_EXIT, BENCHMARK_SIZE}; std::vector tooLong = {5, 15, 23, 24, 29}; //The list of problems that take "too long" to run. (Over 1 second on my machine) //The driver function for the benchmark selection void benchmarkMenu(){ int selection = 0; printBenchmarkMenu(); selection = getBenchmarkMenuSelection(); switch(selection){ case BENCHMARK_OPTIONS::RUN_SPECIFIC: runSpecific(); break; case BENCHMARK_OPTIONS::RUN_ALL_SHORT: runAllShort(); break; case BENCHMARK_OPTIONS::RUN_ALL: runAll(); break; case BENCHMARK_OPTIONS::BENCHMARK_EXIT: break; } } //Print the benchmark menu void printBenchmarkMenu(){ std::cout << "1. Run a specific problem\n" << "2. Run all problems that have a reasonably short run time\n" << "3. Run all problems\n" << "4. Exit the menu" << std::endl; } //Returns a valid menu option int getBenchmarkMenuSelection(){ int selection = 0; std::cin >> selection; while(std::cin.fail() || !isValidBenchmarkMenu(selection)){ std::cout << "That is an invalid option!\nPress Enter to continue" << std::endl; std::cin.clear(); std::cin.get(); printBenchmarkMenu(); std::cin >> selection; } return selection; } //Determines if a value is a valid menu option. Helper for getBenchmarkMenuSelection bool isValidBenchmarkMenu(int selection){ if((selection > 0) && (selection < BENCHMARK_OPTIONS::BENCHMARK_SIZE)){ return true; } else{ return false; } } //Determines which problem user wants to run and runs it void runSpecific(){ //Ask which problem the user wants to run unsigned int problemNumber = getProblemNumber(); //Ask how many times to run the problem unsigned int timesToRun = getNumberOfTimesToRun(); //Get the problem and print its description Problem* problem = getProblem(problemNumber); std::cout << problemNumber << ". " << problem->getDescription() << '\n'; //Run the problem the specific number of times double totalTime = runProblem(problem, timesToRun); //Print the results std::cout << getBenchmarkResults(problem, totalTime, timesToRun); //Release the memory the problem is in delete problem; } //Runs all problems except a few that are specified because of run length void runAllShort(){ //Ask how many times to run the problem unsigned int timesToRun = getNumberOfTimesToRun(); //Run through all valid problem numbers, skipping a few that are in the tooLong vector for(unsigned int cnt = 1;cnt < PROBLEM_NUMBERS.size();++cnt){ unsigned int problemNumber = PROBLEM_NUMBERS[cnt]; //If the problem number is contained in the list of problems that take too long skip it if(mee::isFound(tooLong, problemNumber)){ continue; } //Get the problem and print its description Problem* problem = getProblem(problemNumber); std::cout << problemNumber << ". " << problem->getDescription() << '\n'; //Run the problem the specified number of times double totalTime = runProblem(problem, timesToRun); //Print the results std::cout << getBenchmarkResults(problem, totalTime, timesToRun); //Release the memory the problem is in delete problem; } } //Runs all problems void runAll(){ //Ask how many times to run the problem unsigned int timesToRun = getNumberOfTimesToRun(); //Run through all valid problem numbers, skipping a few that are in the tooLong vector for(unsigned int cnt = 1;cnt < PROBLEM_NUMBERS.size();++cnt){ unsigned int problemNumber = PROBLEM_NUMBERS[cnt]; //Get the problem Problem* problem = getProblem(problemNumber); //Run the problem the specified number of times std::cout << problemNumber << ". " << problem->getDescription() << '\n'; double totalTime = runProblem(problem, timesToRun); //Print the results std::cout << getBenchmarkResults(problem, totalTime, timesToRun); //Release the memory the problem is in delete problem; } } //Asks how many times a problem is supposed to run and returns the value unsigned int getNumberOfTimesToRun(){ unsigned int numOfTimesToRun = 1; std::cout << "How many times do you want to run this problem? "; std::cin >> numOfTimesToRun; while((std::cin.fail()) || (numOfTimesToRun < 1)){ std::cout << "That is an invalid number!\nHow many times do you want to run this problem? "; std::cin.clear(); std::cin >> numOfTimesToRun; } return numOfTimesToRun; } //Runs the problem the given number of times double runProblem(Problem* problem, unsigned int timesToRun){ double totalTime = 0; std::cout << "Solving"; for(unsigned int cnt = 0;cnt < timesToRun;++cnt){ //Reset the data so you are actually counting the run time a second time problem->reset(); (std::cout << '.').flush(); //Solve the problem problem->solve(); //Get the time data totalTime += (problem->getTimer()).getNano(); } return totalTime; } //Prints the benchmark results of a problem std::string getBenchmarkResults(Problem* problem, double totalTime, unsigned int timesRun){ //Calculate the average run time of the problem totalTime /= timesRun; std::string timeResults = mee::Stopwatch::getStr(totalTime); //Tally the results std::stringstream results; results << "\n\n" << problem->getResult(); results << "\nIt took an average of " << timeResults << " to run this problem over " << timesRun << " iterations\n\n" << std::endl; return results.str(); } #endif //BENCHMARK_HPP