/***************************************************************************** * Copyright (c) 2014-2023 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ #include "CommandLine.hpp" #ifdef USE_BENCHMARK # include "../Context.h" # include "../GameState.h" # include "../OpenRCT2.h" # include "../core/File.h" # include "../platform/Platform.h" # include # include # include # include # include using namespace OpenRCT2; static void BM_update(benchmark::State& state, const std::string& filename) { std::unique_ptr context(CreateContext()); if (context->Initialise()) { if (!filename.empty() && !context->LoadParkFromFile(filename)) { state.SkipWithError("Failed to load file!"); } std::vector timings(1); timings.reserve(100); int currentTimingIdx = 0; for (auto _ : state) { if (timings[currentTimingIdx].CurrentIdx == (LOGIC_UPDATE_MEASUREMENTS_COUNT - 1)) { timings.resize(timings.size() + 1); currentTimingIdx++; } LogicTimings* timingToUse = &timings[currentTimingIdx]; context->GetGameState()->UpdateLogic(timingToUse); } state.SetItemsProcessed(state.iterations()); auto accumulator = [timings](LogicTimePart part) -> double { std::chrono::duration timesum; for (const auto& timing : timings) { timesum = std::accumulate( timing.TimingInfo.at(part).begin(), timing.TimingInfo.at(part).end(), std::chrono::duration()); } return std::chrono::duration_cast(timesum).count(); }; state.counters["NetworkUpdateAcc_ms"] = accumulator(LogicTimePart::NetworkUpdate); state.counters["DateAcc_ms"] = accumulator(LogicTimePart::Date); state.counters["ScenarioAcc_ms"] = accumulator(LogicTimePart::Scenario); state.counters["ClimateAcc_ms"] = accumulator(LogicTimePart::Climate); state.counters["MapTilesAcc_ms"] = accumulator(LogicTimePart::MapTiles); state.counters["MapStashProvisionalElementsAcc_ms"] = accumulator(LogicTimePart::MapStashProvisionalElements); state.counters["MapPathWideFlagsAcc_ms"] = accumulator(LogicTimePart::MapPathWideFlags); state.counters["PeepAcc_ms"] = accumulator(LogicTimePart::Peep); state.counters["MapRestoreProvisionalElementsAcc_ms"] = accumulator(LogicTimePart::MapRestoreProvisionalElements); state.counters["VehicleAcc_ms"] = accumulator(LogicTimePart::Vehicle); state.counters["MiscAcc_ms"] = accumulator(LogicTimePart::Misc); state.counters["RideAcc_ms"] = accumulator(LogicTimePart::Ride); state.counters["ParkAcc_ms"] = accumulator(LogicTimePart::Park); state.counters["ResearchAcc_ms"] = accumulator(LogicTimePart::Research); state.counters["RideRatingsAcc_ms"] = accumulator(LogicTimePart::RideRatings); state.counters["RideMeasurmentsAcc_ms"] = accumulator(LogicTimePart::RideMeasurments); state.counters["NewsAcc_ms"] = accumulator(LogicTimePart::News); state.counters["MapAnimationAcc_ms"] = accumulator(LogicTimePart::MapAnimation); state.counters["SoundsAcc_ms"] = accumulator(LogicTimePart::Sounds); state.counters["GameActionsAcc_ms"] = accumulator(LogicTimePart::GameActions); state.counters["NetworkFlushAcc_ms"] = accumulator(LogicTimePart::NetworkFlush); state.counters["ScriptsAcc_ms"] = accumulator(LogicTimePart::Scripts); } else { state.SkipWithError("Context initialization failed."); } } static int CommandLineForBenchSpriteSort(int argc, const char* const* argv) { // Add a baseline test on an empty park benchmark::RegisterBenchmark("baseline", BM_update, std::string{}); // Google benchmark does stuff to argv. It doesn't modify the pointees, // but it wants to reorder the pointers, so present a copy of them. std::vector argv_for_benchmark; // argv[0] is expected to contain the binary name. It's only for logging purposes, don't bother. argv_for_benchmark.push_back(nullptr); // Extract file names from argument list. If there is no such file, consider it benchmark option. for (int i = 0; i < argc; i++) { if (File::Exists(argv[i])) { // Register benchmark for sv6 if valid benchmark::RegisterBenchmark(argv[i], BM_update, argv[i]); } else { argv_for_benchmark.push_back(const_cast(argv[i])); } } // Update argc with all the changes made argc = static_cast(argv_for_benchmark.size()); ::benchmark::Initialize(&argc, &argv_for_benchmark[0]); if (::benchmark::ReportUnrecognizedArguments(argc, &argv_for_benchmark[0])) return -1; Platform::CoreInit(); gOpenRCT2Headless = true; ::benchmark::RunSpecifiedBenchmarks(); return 0; } static exitcode_t HandleBenchUpdate(CommandLineArgEnumerator* argEnumerator) { const char* const* argv = static_cast(argEnumerator->GetArguments()) + argEnumerator->GetIndex(); int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex(); int32_t result = CommandLineForBenchSpriteSort(argc, argv); if (result < 0) { return EXITCODE_FAIL; } return EXITCODE_OK; } #else static exitcode_t HandleBenchUpdate(CommandLineArgEnumerator* argEnumerator) { LOG_ERROR("Sorry, Google benchmark not enabled in this build"); return EXITCODE_FAIL; } #endif // USE_BENCHMARK const CommandLineCommand CommandLine::BenchUpdateCommands[]{ #ifdef USE_BENCHMARK DefineCommand( "", "... [--benchmark_list_tests={true|false}] [--benchmark_filter=] [--benchmark_min_time=] " "[--benchmark_repetitions=] [--benchmark_report_aggregates_only={true|false}] " "[--benchmark_format=] [--benchmark_out=] [--benchmark_out_format=] " "[--benchmark_color={auto|true|false}] [--benchmark_counters_tabular={true|false}] [--v=]", nullptr, HandleBenchUpdate), CommandTableEnd #else DefineCommand("", "*** SORRY NOT ENABLED IN THIS BUILD ***", nullptr, HandleBenchUpdate), CommandTableEnd #endif // USE_BENCHMARK };