/*
This file is part of Ingen.
Copyright 2007-2017 David Robillard
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or any later version.
Ingen 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 Affero General Public License for details.
You should have received a copy of the GNU Affero General Public License
along with Ingen. If not, see .
*/
#include "ingen/Atom.hpp"
#include "ingen/Clock.hpp"
#include "ingen/Configuration.hpp"
#include "ingen/EngineBase.hpp"
#include "ingen/Forge.hpp"
#include "ingen/Parser.hpp"
#include "ingen/World.hpp"
#include "ingen/runtime_paths.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
namespace ingen::bench {
namespace {
std::unique_ptr world;
void
ingen_try(bool cond, const char* msg)
{
if (!cond) {
std::cerr << "ingen: Error: " << msg << "\n";
world.reset();
exit(EXIT_FAILURE);
}
}
std::string
real_path(const char* path)
{
char* const c_real_path = realpath(path, nullptr);
std::string result(c_real_path ? c_real_path : "");
free(c_real_path);
return result;
}
int
run(int argc, char** argv)
{
// Create world
try {
world = std::make_unique(nullptr, nullptr, nullptr);
world->conf().add(
"output", "output", 'O', "File to write benchmark output",
ingen::Configuration::SESSION, world->forge().String, Atom());
world->load_configuration(argc, argv);
} catch (std::exception& e) {
std::cout << "ingen: " << e.what() << "\n";
return EXIT_FAILURE;
}
// Get mandatory command line arguments
const Atom& load = world->conf().option("load");
const Atom& out = world->conf().option("output");
if (!load.is_valid() || !out.is_valid()) {
std::cerr << "Usage: ingen_bench --load START_GRAPH --output OUT_FILE\n";
return EXIT_FAILURE;
}
// Get start graph and output file options
const std::string start_graph =
real_path(static_cast(load.get_body()));
const std::string out_file = static_cast(out.get_body());
if (start_graph.empty()) {
std::cerr << "error: initial graph '"
<< static_cast(load.get_body())
<< "' does not exist\n";
return EXIT_FAILURE;
}
// Load modules
ingen_try(world->load_module("server"),
"Unable to load server module");
// Initialise engine
ingen_try(!!world->engine(),
"Unable to create engine");
world->engine()->init(48000.0, 4096, 4096);
world->engine()->activate();
// Load graph
if (!world->parser()->parse_file(*world, *world->interface(), start_graph)) {
std::cerr << "error: failed to load initial graph " << start_graph
<< "\n";
return EXIT_FAILURE;
}
world->engine()->flush_events(std::chrono::milliseconds(20));
// Run benchmark
// TODO: Set up real-time scheduling for this and worker threads
const ingen::Clock clock;
const uint32_t n_test_frames = 1 << 20;
const uint32_t block_length = 4096;
const uint64_t t_start = clock.now_microseconds();
for (uint32_t i = 0; i < n_test_frames; i += block_length) {
world->engine()->advance(block_length);
world->engine()->run(block_length);
//world->engine()->main_iteration();
}
const uint64_t t_end = clock.now_microseconds();
// Write log output
const std::unique_ptr log{fopen(out_file.c_str(), "a"),
&fclose};
if (ftell(log.get()) == 0) {
fprintf(log.get(), "# n_threads\trun_time\treal_time\n");
}
fprintf(log.get(), "%u\t%f\t%f\n",
world->conf().option("threads").get(),
static_cast(t_end - t_start) / 1000000.0,
(n_test_frames / 48000.0));
// Shut down
world->engine()->deactivate();
return EXIT_SUCCESS;
}
} // namespace
} // namespace ingen::bench
int
main(int argc, char** argv)
{
ingen::set_bundle_path_from_code(
reinterpret_cast(&ingen::bench::ingen_try));
return ingen::bench::run(argc, argv);
}