Skip to content

Commit

Permalink
Merge pull request #508 from contour-terminal/feature/bench-headless-cli
Browse files Browse the repository at this point in the history
Feature/bench headless cli
  • Loading branch information
christianparpart authored Nov 18, 2021
2 parents 03f00e5 + 9e64768 commit 5c6a630
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 57 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Adds key bindings to default configuration to allow simply pressing Ctrl+C/Ctrl+V (without Shift modifier) when an active selection is present.
- Adds process current working directory on macOS.
- Adds `contour license` CLI command to show project license but also an overview of all dependencies.
- Adds a proper CLI to `bench-headless`. Building the headless benchmark tool is not shipped by default.

### 0.2.1 (2021-11-14)

Expand Down
7 changes: 7 additions & 0 deletions src/terminal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ if(LIBTERMINAL_TESTING)
add_test(terminal_test ./terminal_test)

add_executable(bench-headless bench-headless.cpp)
target_compile_definitions(bench-headless PRIVATE
CONTOUR_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
CONTOUR_VERSION_MINOR=${PROJECT_VERSION_MINOR}
CONTOUR_VERSION_PATCH=${PROJECT_VERSION_PATCH}
CONTOUR_VERSION_STRING="${CONTOUR_VERSION_STRING}"
CONTOUR_PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}"
)
target_link_libraries(bench-headless fmt::fmt-header-only terminal termbench)
endif()

Expand Down
217 changes: 160 additions & 57 deletions src/terminal/bench-headless.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <terminal/logging.h>
#include <terminal/pty/MockViewPty.h>

#include <crispy/App.h>
#include <crispy/CLI.h>

#include <libtermbench/termbench.h>

#include <iostream>
Expand Down Expand Up @@ -50,16 +53,35 @@ class NullParserEvents: public terminal::ParserEvents
void dispatchAPC() override {}
};

struct BenchOptions
{
unsigned testSizeMB = 64;
bool manyLines = false;
bool longLines = false;
bool sgr = false;
bool binary = false;
};

template <typename Writer>
void baseBenchmark(Writer&& _writer, size_t _testSizeMB, string_view _title)
int baseBenchmark(Writer&& _writer, BenchOptions _options, string_view _title)
{
auto const titleText = fmt::format("Running benchmark: {}", _title);
std::cout << titleText << '\n'
<< std::string(titleText.size(), '=') << '\n';
if (!(_options.binary || _options.longLines || _options.manyLines || _options.sgr))
{
cout << "No test cases specified. Defaulting to: cat, long, sgr.\n";
_options.manyLines = true;
_options.longLines = true;
_options.sgr = true;
}

auto const titleText = fmt::format("Running benchmark: {} (test size: {} MB)",
_title, _options.testSizeMB);

cout << titleText << '\n'
<< string(titleText.size(), '=') << '\n';

auto tbp = contour::termbench::Benchmark{
std::forward<Writer>(_writer),
_testSizeMB,
_options.testSizeMB,
80,
24,
[&](contour::termbench::Test const& _test)
Expand All @@ -68,11 +90,20 @@ void baseBenchmark(Writer&& _writer, size_t _testSizeMB, string_view _title)
}
};

tbp.add(contour::termbench::tests::many_lines());
tbp.add(contour::termbench::tests::long_lines());
tbp.add(contour::termbench::tests::sgr_fg_lines());
tbp.add(contour::termbench::tests::sgr_fgbg_lines());
// tbp.add(contour::termbench::tests::binary());
if (_options.manyLines)
tbp.add(contour::termbench::tests::many_lines());

if (_options.longLines)
tbp.add(contour::termbench::tests::long_lines());

if (_options.sgr)
{
tbp.add(contour::termbench::tests::sgr_fg_lines());
tbp.add(contour::termbench::tests::sgr_fgbg_lines());
}

if (_options.binary)
tbp.add(contour::termbench::tests::binary());

tbp.runAll();

Expand All @@ -81,58 +112,130 @@ void baseBenchmark(Writer&& _writer, size_t _testSizeMB, string_view _title)
cout << "-------\n";
tbp.summarize(cout);
cout << '\n';
}

void benchmarkParserOnly()
{
auto po = NullParserEvents{};
auto parser = terminal::parser::Parser{po};
baseBenchmark(
[&](char const* a, size_t b)
{
parser.parseFragment(string_view(a, b));
},
1024, // MB
"Parser only"sv
);
return EXIT_SUCCESS;
}

void benchmarkTerminal()
{
auto const testSizeMB = 32;
auto pageSize = terminal::PageSize{terminal::LineCount(25), terminal::ColumnCount(80)};
auto const ptyReadBufferSize = 8192;
auto maxHistoryLineCount = terminal::LineCount(4096);
auto eh = terminal::Terminal::Events{};
auto pty = std::make_unique<terminal::MockViewPty>(pageSize);
auto vt = terminal::Terminal{
*pty,
ptyReadBufferSize,
eh,
maxHistoryLineCount
};
vt.screen().setMode(terminal::DECMode::AutoWrap, true);
namespace CLI = crispy::cli;

baseBenchmark(
[&](char const* a, size_t b)
{
pty->setReadData({a, b});
do vt.processInputOnce();
while (!pty->stdoutBuffer().empty());
},
testSizeMB,
"terminal with screen buffer"
);

cout << fmt::format("{:>12}: {}\n\n",
"history size",
vt.screen().maxHistoryLineCount().value_or(terminal::LineCount(0)));
}
class ContourHeadlessBench: public crispy::App
{
public:
ContourHeadlessBench():
App("bench-headless", "Contour Headless Benchmark", CONTOUR_VERSION_STRING, "Apache-2.0")
{
using Project = crispy::cli::about::Project;
crispy::cli::about::registerProjects(
#if defined(CONTOUR_BUILD_WITH_MIMALLOC)
Project{"mimalloc", "", ""},
#endif
Project{"range-v3", "Boost Software License 1.0", "https://github.com/ericniebler/range-v3"},
Project{"yaml-cpp", "MIT", "https://github.com/jbeder/yaml-cpp"},
Project{"termbench-pro", "Apache-2.0", "https://github.com/contour-terminal/termbench-pro"},
Project{"fmt", "MIT", "https://github.com/fmtlib/fmt"}
);
link("bench-headless.parser", bind(&ContourHeadlessBench::benchParserOnly, this));
link("bench-headless.grid", bind(&ContourHeadlessBench::benchGrid, this));
link("bench-headless.meta", bind(&ContourHeadlessBench::showMetaInfo, this));
}

crispy::cli::Command parameterDefinition() const override
{
auto const perfOptions =
CLI::OptionList{
CLI::Option{"size", CLI::Value{32u}, "Number of megabyte to process per test.", "MB"},
CLI::Option{"cat", CLI::Value{false}, "Enable cat-style short-line ASCII stream test."},
CLI::Option{"long", CLI::Value{false}, "Enable long-line ASCII stream test."},
CLI::Option{"sgr", CLI::Value{false}, "Enable SGR stream test."},
CLI::Option{"binary", CLI::Value{false}, "Enable binary stream test."},
};

return CLI::Command{
"bench-headless",
"Contour Terminal Emulator " CONTOUR_VERSION_STRING " - https://github.com/contour-terminal/contour/ ;-)",
CLI::OptionList{},
CLI::CommandList{
CLI::Command{"help", "Shows this help and exits."},
CLI::Command{"meta", "Shows some terminal backend meta information and exits."},
CLI::Command{"version", "Shows the version and exits."},
CLI::Command{"license", "Shows the license, and project URL of the used projects and Contour."},
CLI::Command{"grid", "Shows the license, and project URL of the used projects and Contour.", perfOptions},
CLI::Command{"parser", "Shows the license, and project URL of the used projects and Contour.", perfOptions},
}
};
}

int showMetaInfo()
{
// Show any interesting meta information.
fmt::print("Cell : {} bytes\n", sizeof(terminal::Cell));
//fmt::print("CellExtra : {} bytes\n", sizeof(terminal::CellExtra));
fmt::print("CellFlags : {} bytes\n", sizeof(terminal::CellFlags));
fmt::print("Color : {} bytes\n", sizeof(terminal::Color));
return EXIT_SUCCESS;
}

BenchOptions benchOptionsFor(string_view _kind)
{
auto const prefix = fmt::format("bench-headless.{}.", _kind);
auto opts = BenchOptions{};
opts.testSizeMB = parameters().uint(prefix + "size");
opts.manyLines = parameters().boolean(prefix + "cat");
opts.longLines = parameters().boolean(prefix + "long");
opts.sgr = parameters().boolean(prefix + "sgr");
opts.binary = parameters().boolean(prefix + "binary");
return opts;
}

int benchGrid()
{
auto pageSize = terminal::PageSize{terminal::LineCount(25), terminal::ColumnCount(80)};
auto const ptyReadBufferSize = 10000;
auto maxHistoryLineCount = terminal::LineCount(4096);
auto eh = terminal::Terminal::Events{};
auto pty = std::make_unique<terminal::MockViewPty>(pageSize);
auto vt = terminal::Terminal{
*pty,
ptyReadBufferSize,
eh,
maxHistoryLineCount
};
vt.screen().setMode(terminal::DECMode::AutoWrap, true);

auto const rv = baseBenchmark(
[&](char const* a, size_t b)
{
pty->setReadData({a, b});
do vt.processInputOnce();
while (!pty->stdoutBuffer().empty());
},
benchOptionsFor("grid"),
"terminal with screen buffer"
);
if (rv == EXIT_SUCCESS)
cout << fmt::format("{:>12}: {}\n\n",
"history size",
*vt.screen().maxHistoryLineCount());
return rv;
}

int benchParserOnly()
{
auto po = NullParserEvents{};
auto parser = terminal::parser::Parser{po};
return baseBenchmark(
[&](char const* a, size_t b)
{
parser.parseFragment(string_view(a, b));
},
benchOptionsFor("parser"),
"Parser only"
);
}
};

int main(int argc, char const* argv[])
{
benchmarkTerminal();
benchmarkParserOnly();

return EXIT_SUCCESS;
ContourHeadlessBench app;
return app.run(argc, argv);
}

0 comments on commit 5c6a630

Please sign in to comment.