Skip to content

Commit

Permalink
Merge pull request #16 from contour-terminal/further-cleanups
Browse files Browse the repository at this point in the history
termbench: Always write full frames and add `--from-file FILE` to the tb CLI
  • Loading branch information
christianparpart authored Mar 18, 2024
2 parents cf92f9e + ea3a3b2 commit 760c9c3
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 82 deletions.
132 changes: 73 additions & 59 deletions libtermbench/termbench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ void Benchmark::add(std::unique_ptr<Test> _test)
tests_.emplace_back(std::move(_test));
}

void Benchmark::updateWindowTitle(std::string_view _title)
{
auto const now = steady_clock::now();
if (now - lastWindowTitleUpdate_ < 100ms)
return;

lastWindowTitleUpdate_ = now;
std::cout << std::format("\033]2;{}\033\\", _title);
std::cout.flush();
}


void Benchmark::writeOutput(Buffer const& testBuffer)
{
auto const output = testBuffer.output();
Expand All @@ -80,7 +92,16 @@ void Benchmark::runAll()
beforeTest_(*test);

test->setup(terminalSize_);
test->run(*buffer);

while (buffer->good())
{
test->fill(*buffer);

updateWindowTitle(std::format("{}: filling buffer {:.3}%",
test->name,
static_cast<double>(buffer->size())
/ static_cast<double>(1024 * 1024 * testSizeMB_)));
}

auto const beginTime = steady_clock::now();
writeOutput(*buffer);
Expand Down Expand Up @@ -197,6 +218,20 @@ namespace
writeChar(_sink, 'm');
}

class CraftedTest: public Test
{
public:
explicit CraftedTest(std::string name, std::string description, std::string text) noexcept:
Test(std::move(name), std::move(description)), _text { std::move(text) }
{
}

void fill(Buffer& _sink) noexcept override { _sink.write(_text); }

private:
std::string _text;
};

class ManyLines: public Test
{
public:
Expand All @@ -215,11 +250,7 @@ namespace
}
}

void run(Buffer& _sink) noexcept override
{
while (_sink.good())
_sink.write(text);
}
void fill(Buffer& _sink) noexcept override { _sink.write(text); }

private:
std::string text;
Expand All @@ -230,13 +261,7 @@ namespace
public:
LongLines() noexcept: Test("long_lines", "") {}

void run(Buffer& _sink) noexcept override
{
while (_sink.good())
{
writeChar(_sink, randomAsciiChar());
}
}
void fill(Buffer& _sink) noexcept override { writeChar(_sink, randomAsciiChar()); }
};

class SgrFgColoredText: public Test
Expand All @@ -245,25 +270,24 @@ namespace
SgrFgColoredText() noexcept: Test("sgr_fg_lines", "") {}

TerminalSize terminalSize;
unsigned frameID = 0;

void setup(TerminalSize size) noexcept override { terminalSize = size; }

void run(Buffer& _sink) noexcept override
void fill(Buffer& _sink) noexcept override
{
for (unsigned frameID = 0; _sink.good(); ++frameID)
++frameID;
for (u16 y = 0; y < terminalSize.lines; ++y)
{
for (u16 y = 0; y < terminalSize.lines; ++y)
moveCursor(_sink, 1, y + 1u);
for (u16 x = 0; x < terminalSize.columns; ++x)
{
moveCursor(_sink, 1, y + 1u);
for (u16 x = 0; x < terminalSize.columns; ++x)
{
auto const r = frameID;
auto const g = frameID + y;
auto const b = frameID + y + x;

setTextColor(_sink, r & 0xff, g & 0xff, b & 0xff);
writeChar(_sink, static_cast<char>('a' + (frameID + x + y) % ('z' - 'a')));
}
auto const r = frameID;
auto const g = frameID + y;
auto const b = frameID + y + x;

setTextColor(_sink, r & 0xff, g & 0xff, b & 0xff);
writeChar(_sink, static_cast<char>('a' + (frameID + x + y) % ('z' - 'a')));
}
}
}
Expand All @@ -275,30 +299,28 @@ namespace
SgrFgBgColoredText() noexcept: Test("sgr_fg_bg_lines", "") {}

TerminalSize terminalSize;
unsigned frameID = 0;

void setup(TerminalSize size) noexcept override { terminalSize = size; }

void run(Buffer& _sink) noexcept override
void fill(Buffer& _sink) noexcept override
{
for (unsigned frameID = 0; _sink.good(); ++frameID)
for (u16 y = 0; y < terminalSize.lines; ++y)
{
for (u16 y = 0; y < terminalSize.lines; ++y)
moveCursor(_sink, 1, y + 1u);
for (u16 x = 0; x < terminalSize.columns; ++x)
{
moveCursor(_sink, 1, y + 1u);
for (u16 x = 0; x < terminalSize.columns; ++x)
{
auto r = static_cast<uint8_t>(frameID);
auto g = static_cast<uint8_t>(frameID + y);
auto b = static_cast<uint8_t>(frameID + y + x);
setTextColor(_sink, r, g, b);

r = static_cast<uint8_t>(frameID + y + x);
g = static_cast<uint8_t>(frameID + y);
b = static_cast<uint8_t>(frameID);
setBackgroundColor(_sink, r, g, b);

writeChar(_sink, static_cast<char>('a' + (frameID + x + y) % ('z' - 'a')));
}
auto r = static_cast<uint8_t>(frameID);
auto g = static_cast<uint8_t>(frameID + y);
auto b = static_cast<uint8_t>(frameID + y + x);
setTextColor(_sink, r, g, b);

r = static_cast<uint8_t>(frameID + y + x);
g = static_cast<uint8_t>(frameID + y);
b = static_cast<uint8_t>(frameID);
setBackgroundColor(_sink, r, g, b);

writeChar(_sink, static_cast<char>('a' + (frameID + x + y) % ('z' - 'a')));
}
}
}
Expand All @@ -322,13 +344,7 @@ namespace
}
}

void run(Buffer& _sink) noexcept override
{
while (_sink.good())
{
_sink.write(text);
}
}
void fill(Buffer& _sink) noexcept override { _sink.write(text); }

void teardown(Buffer& _sink) noexcept override { _sink.write("\033c"); }

Expand All @@ -342,14 +358,7 @@ namespace
Line(std::string name, std::string text): Test(name, ""), text { text } {}
void setup(TerminalSize) override {}

void run(Buffer& _sink) noexcept override
{
for (size_t i = 0; i < 1000; ++i)
{
while (_sink.good())
_sink.write(text);
}
}
void fill(Buffer& _sink) noexcept override { _sink.write(text); }

private:
std::string text;
Expand Down Expand Up @@ -410,4 +419,9 @@ std::unique_ptr<Test> sgrbg_line(size_t N)
return std::make_unique<Line>(name, text);
}

std::unique_ptr<Test> crafted(std::string name, std::string description, std::string text)
{
return std::make_unique<CraftedTest>(std::move(name), std::move(description), std::move(text));
}

} // namespace termbench::tests
37 changes: 17 additions & 20 deletions libtermbench/termbench.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/
#pragma once

#include <array>
#include <chrono>
#include <cstring>
#include <functional>
Expand All @@ -36,32 +35,27 @@ struct TerminalSize
struct Buffer
{
public:
explicit Buffer(size_t _maxWriteSizeMB) noexcept: maxWriteSize { _maxWriteSizeMB * 1024 * 1024 } {}
explicit Buffer(size_t _maxWriteSizeMB) noexcept: _maxSize { _maxWriteSizeMB * 1024 * 1024 } {}

bool good() const noexcept { return nwritten < maxWriteSize; }
bool good() const noexcept { return _data.size() < _maxSize; }

void write(std::string_view _data) noexcept
bool write(std::string_view chunk) noexcept
{
auto const n = nwritten + _data.size() < maxWriteSize ? _data.size() : maxWriteSize - nwritten;
auto i = _data.data();
auto p = data.data() + nwritten;
auto e = data.data() + nwritten + n;
while (p != e)
*p++ = *i++;
// std::memcpy(data.data() + nwritten, _data.data(), n);
nwritten += n;
if (_data.size() > _maxSize)
return false;
_data.append(chunk.data(), chunk.size());
return true;
}

std::string_view output() const noexcept { return std::string_view { data.data(), nwritten }; }
std::string_view output() const noexcept { return std::string_view { _data.data(), _data.size() }; }

void clear() noexcept { nwritten = 0; }
bool empty() const noexcept { return nwritten == 0; }
void clear() noexcept { _data.clear(); }
bool empty() const noexcept { return _data.empty(); }
size_t size() const noexcept { return _data.size(); }

private:
size_t maxWriteSize = 4 * 1024 * 1024;

std::array<char, 64 * 1024 * 1024> data {};
size_t nwritten = 0;
std::string _data;
std::size_t _maxSize;
};

/// Describes a single test.
Expand All @@ -77,7 +71,7 @@ struct Test
}

virtual void setup(TerminalSize /*terminalSize*/) {}
virtual void run(Buffer& /*stdoutBuffer*/) noexcept = 0;
virtual void fill(Buffer& /*stdoutBuffer*/) noexcept = 0;
virtual void teardown(Buffer& /*stdoutBuffer*/) {}
};

Expand Down Expand Up @@ -108,11 +102,13 @@ class Benchmark

private:
void writeOutput(Buffer const& testBuffer);
void updateWindowTitle(std::string_view _title);

std::function<void(char const*, size_t)> writer_;
std::function<void(Test const&)> beforeTest_;
size_t testSizeMB_;
TerminalSize terminalSize_;
std::chrono::steady_clock::time_point lastWindowTitleUpdate_;

std::vector<std::unique_ptr<Test>> tests_;
std::vector<Result> results_;
Expand All @@ -131,4 +127,5 @@ std::unique_ptr<Test> binary();
std::unique_ptr<Test> ascii_line(size_t);
std::unique_ptr<Test> sgr_line(size_t);
std::unique_ptr<Test> sgrbg_line(size_t);
std::unique_ptr<Test> crafted(std::string name, std::string description, std::string text);
} // namespace termbench::tests
46 changes: 43 additions & 3 deletions tb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <format>
#include <fstream>
#include <functional>
Expand Down Expand Up @@ -102,6 +103,7 @@ struct BenchSettings
size_t testSizeMB = 32;
bool nullSink = false;
bool stdoutFastPath = false;
std::vector<std::filesystem::path> craftedTests {};
bool columnByColumn = false;
std::string fileout {};
std::optional<int> earlyExitCode = std::nullopt;
Expand Down Expand Up @@ -144,7 +146,7 @@ BenchSettings parseArguments(int argc, char const* argv[], TerminalSize const& i
else if (argv[i] == "--help"sv || argv[i] == "-h"sv)
{
cout << std::format("{} [--null-sink] [--fixed-size] [--stdout-fastpath] [--column-by-column] "
"[--size MB] [--output FILE] [--help]\n",
"[--size MB] [--from-file FILE] [--output FILE] [--help]\n",
argv[0]);
return { .earlyExitCode = EXIT_SUCCESS };
}
Expand All @@ -153,6 +155,16 @@ BenchSettings parseArguments(int argc, char const* argv[], TerminalSize const& i
++i;
settings.fileout = argv[i];
}
else if (argv[i] == "--from-file"sv && i + 1 < argc)
{
++i;
if (!std::filesystem::exists(argv[i]))
{
cerr << std::format("Failed to open file '{}'.\n", argv[i]);
return { .earlyExitCode = EXIT_FAILURE };
}
settings.craftedTests.emplace_back(argv[i]);
}
else
{
cerr << std::format("Invalid argument usage.\n");
Expand All @@ -162,13 +174,38 @@ BenchSettings parseArguments(int argc, char const* argv[], TerminalSize const& i
return settings;
}

void addTestsToBenchmark(termbench::Benchmark& tb, BenchSettings const& settings)
std::string loadFileContents(std::filesystem::path const& path)
{
std::ifstream file { path };
if (!file)
return {};

std::string content;
std::size_t const fileSize = std::filesystem::file_size(path);
content.resize(fileSize);
file.read(content.data(), static_cast<std::streamsize>(fileSize));
return content;
}

bool addTestsToBenchmark(termbench::Benchmark& tb, BenchSettings const& settings)
{
tb.add(termbench::tests::many_lines());
tb.add(termbench::tests::long_lines());
tb.add(termbench::tests::sgr_fg_lines());
tb.add(termbench::tests::sgr_fgbg_lines());
tb.add(termbench::tests::binary());
// TODO: The above tests should also be configurable via command line (-mlfgb).

for (auto const& test: settings.craftedTests)
{
auto content = loadFileContents(test);
if (content.empty())
{
cerr << std::format("Failed to load file '{}'.\n", test.string());
return false;
}
tb.add(termbench::tests::crafted(test.filename(), "", std::move(content)));
}

if (settings.columnByColumn)
{
Expand All @@ -180,6 +217,8 @@ void addTestsToBenchmark(termbench::Benchmark& tb, BenchSettings const& settings
for (size_t i = 0; i < maxColumns; ++i)
tb.add(termbench::tests::sgrbg_line(i));
}

return true;
}

void changeTerminalSize(TerminalSize requestedTerminalSize)
Expand Down Expand Up @@ -233,7 +272,8 @@ int main(int argc, char const* argv[])
settings.testSizeMB, // MB per test
settings.requestedTerminalSize };

addTestsToBenchmark(tb, settings);
if (!addTestsToBenchmark(tb, settings))
return EXIT_FAILURE;

WithScopedTerminalSize {
initialTerminalSize,
Expand Down

0 comments on commit 760c9c3

Please sign in to comment.