From 50ab531f7ad4bb31f5f79f885448186b32ef6cbe Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Sun, 17 Mar 2024 14:46:24 +0100 Subject: [PATCH] Fixes an infinite loop in termbnech.cpp and add some overall improvements Signed-off-by: Christian Parpart --- libtermbench/termbench.cpp | 37 ++++++++-------- libtermbench/termbench.h | 4 ++ scripts/Xvfb-bench-run.sh | 73 ++++++++++++++++++++++++------- tb/CMakeLists.txt | 7 +++ tb/main.cpp | 90 ++++++++++++++++++++++++++++---------- 5 files changed, 153 insertions(+), 58 deletions(-) diff --git a/libtermbench/termbench.cpp b/libtermbench/termbench.cpp index 16b5f67..65b48ee 100644 --- a/libtermbench/termbench.cpp +++ b/libtermbench/termbench.cpp @@ -66,6 +66,18 @@ void Benchmark::add(unique_ptr _test) tests_.emplace_back(std::move(_test)); } +void Benchmark::writeOutput(Buffer const& testBuffer) +{ + auto const output = testBuffer.output(); + auto remainingBytes = totalSizeBytes(); + while (remainingBytes > 0) + { + auto const n = std::min(output.size(), remainingBytes); + writer_(output.data(), n); + remainingBytes -= n; + } +} + void Benchmark::runAll() { auto buffer = make_unique(min(static_cast(64u), testSizeMB_)); @@ -78,30 +90,19 @@ void Benchmark::runAll() test->setup(width_, height_); test->run(*buffer); - auto const output = buffer->output(); - auto const totalSizeBytes = testSizeMB_ * 1024 * 1024; auto const beginTime = steady_clock::now(); - auto remainingBytes = totalSizeBytes; + writeOutput(*buffer); auto const diff = duration_cast(steady_clock::now() - beginTime); - while (diff < 1s) - { - while (remainingBytes > 0) - { - auto const n = std::min(output.size(), remainingBytes); - writer_(output.data(), n); - remainingBytes -= n; - } - } - results_.emplace_back(Result { *test, diff, totalSizeBytes }); + results_.emplace_back(*test, diff, totalSizeBytes()); buffer->clear(); test->teardown(*buffer); - if (buffer->empty()) - continue; - - writer_(buffer->output().data(), buffer->output().size()); - buffer->clear(); + if (!buffer->empty()) + { + writer_(buffer->output().data(), buffer->output().size()); + buffer->clear(); + } } } // diff --git a/libtermbench/termbench.h b/libtermbench/termbench.h index b613480..fdf6048 100644 --- a/libtermbench/termbench.h +++ b/libtermbench/termbench.h @@ -97,7 +97,11 @@ class Benchmark std::vector const& results() const noexcept { return results_; } + [[constexpr]] size_t totalSizeBytes() const noexcept { return testSizeMB_ * 1024 * 1024; } + private: + void writeOutput(Buffer const& testBuffer); + std::function writer_; std::function beforeTest_; size_t testSizeMB_; diff --git a/scripts/Xvfb-bench-run.sh b/scripts/Xvfb-bench-run.sh index a90d637..e1a6657 100755 --- a/scripts/Xvfb-bench-run.sh +++ b/scripts/Xvfb-bench-run.sh @@ -1,15 +1,61 @@ #!/usr/bin/env bash # -# Usage: Xvfb-contour-run.sh +# Usage: Xvfb-bench-run.sh -set -x +TB_BIN="${1:-${TB_BIN}}" +CONTOUR_BIN="${CONTOUR_BIN:-contour}" +KITTY_BIN="${KITTY_BIN:-kitty}" +XTERM_BIN="${XTERM_BIN:-xterm}" +ALACRITTY_BIN="${ALACRITTY_BIN:-alacritty}" +FB_DISPLAY="${FB_DISPLAY:-:99}" + +OUTPUT_DIR="${PWD}" + +if [[ "$TB_BIN" == "" ]]; then + echo "Usage: $0 " + exit 1 +fi + +function require_bin() { + local tool="${1}" + if ! which "${tool}" >/dev/null; then + echo 1>&2 "$0: Could not find the required tool ${tool}." + exit 1 + fi +} + +require_bin Xvfb +require_bin "${TB_BIN}" +require_bin "${CONTOUR_BIN}" +require_bin "${KITTY_BIN}" +require_bin "${XTERM_BIN}" +require_bin "${ALACRITTY_BIN}" +export TB_BIN=$(realpath $TB_BIN) +export DISPLAY=${FB_DISPLAY} +export LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-true}" -LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-true}" -DISPLAY=:99 -export LIBGL_ALWAYS_SOFTWARE -export DISPLAY +function program_exit() { + local exit_code=$1 + if [[ "$GITHUB_OUTPUT" != "" ]]; then + echo "exitCode=$exit_code" >> "$GITHUB_OUTPUT" + fi + exit $exit_code +} + +function bench_terminal() { + printf "\033[1m==> Running terminal: $1\033[m\n" + local terminal_name=$(basename $1) + time "${@}" -e "${TB_BIN}" --fixed-size --stdout-fastpath --column-by-column --output "${OUTPUT_DIR}/${terminal_name}_results" + local exit_code=$? + printf "\033[1m==> Terminal exit code: $exit_code\033[m\n" + if [[ $exit_code -ne 0 ]]; then + program_exit $exit_code + fi +} + +set -x Xvfb $DISPLAY -screen 0 1280x1024x24 & XVFB_PID=$! @@ -17,14 +63,9 @@ trap "kill $XVFB_PID" EXIT sleep 3 -TB_BIN=$PWD/build/tb/tb +bench_terminal "${CONTOUR_BIN}" display ${DISPLAY} +bench_terminal "${KITTY_BIN}" +bench_terminal "${XTERM_BIN}" -display ${DISPLAY} +bench_terminal "${ALACRITTY_BIN}" -contour display ${DISPLAY} $TB_BIN --output contour_results -mv $HOME/contour_results . -kitty -e $TB_BIN --output kitty_results -xterm -display ${DISPLAY} -e $TB_BIN --output xterm_results -alacritty -e $TB_BIN --output alacritty_results - -if [[ "$GITHUB_OUTPUT" != "" ]]; then - echo "exitCode=$?" >> "$GITHUB_OUTPUT" -fi +program_exit 0 diff --git a/tb/CMakeLists.txt b/tb/CMakeLists.txt index 62a5ffa..becf744 100644 --- a/tb/CMakeLists.txt +++ b/tb/CMakeLists.txt @@ -12,3 +12,10 @@ set_target_properties(tb PROPERTIES install(TARGETS tb RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +add_custom_target(Xvfb-bench-run + COMMAND ${CMAKE_SOURCE_DIR}/scripts/Xvfb-bench-run.sh $ + VERBATIM + USES_TERMINAL + DEPENDS tb + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} +) diff --git a/tb/main.cpp b/tb/main.cpp index b65e6c5..3e165de 100644 --- a/tb/main.cpp +++ b/tb/main.cpp @@ -23,29 +23,39 @@ using std::cerr; using std::cout; -using std::stoul; -using std::tuple; using namespace std::string_view_literals; using namespace std::placeholders; #if !defined(_WIN32) #include + #include #include #else #include #endif +#define STDOUT_FASTPATH_FD 3 + namespace { -std::pair getTerminalSize() noexcept + +struct TerminalSize +{ + unsigned short columns = 0; + unsigned short lines = 0; + + constexpr auto operator<=>(TerminalSize const&) const noexcept = default; +}; + +TerminalSize getTerminalSize() noexcept { - auto const DefaultSize = std::pair { 80, 24 }; + auto const DefaultSize = TerminalSize { 80, 24 }; #if !defined(_WIN32) winsize ws; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) return DefaultSize; return { ws.ws_col, ws.ws_row }; #else @@ -58,6 +68,7 @@ void nullWrite(char const*, size_t) { } +template void chunkedWriteToStdout(char const* _data, size_t _size) { auto constexpr PageSize = 4096; // 8192; @@ -70,7 +81,7 @@ void chunkedWriteToStdout(char const* _data, size_t _size) while (_size >= PageSize) { #if !defined(_WIN32) - auto const n = write(STDOUT_FILENO, _data, PageSize); + auto const n = write(StdoutFileNo, _data, PageSize); if (n < 0) perror("write"); _data += n; @@ -83,7 +94,7 @@ void chunkedWriteToStdout(char const* _data, size_t _size) #if !defined(_WIN32) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-result" - write(STDOUT_FILENO, _data, _size); + write(StdoutFileNo, _data, _size); #pragma GCC diagnostic pop #else WriteConsoleA(stdoutHandle, _data, static_cast(_size), &nwritten, nullptr); @@ -101,9 +112,12 @@ int main(int argc, char const* argv[]) #endif // TODO: Also run against NullSink to get a base value. - auto [width, height] = getTerminalSize(); + auto const initialTerminalSize = getTerminalSize(); + auto requestedTerminalSize = initialTerminalSize; size_t testSizeMB = 32; bool nullSink = false; + bool stdoutFastPath = false; + bool columnByColumn = false; std::string fileout {}; for (int i = 1; i < argc; ++i) @@ -113,14 +127,33 @@ int main(int argc, char const* argv[]) cout << std::format("Using null-sink.\n"); nullSink = true; } + else if (argv[i] == "--fixed-size"sv) + { + requestedTerminalSize.columns = 200; + requestedTerminalSize.lines = 30; + } + else if (argv[i] == "--stdout-fastpath"sv) + { +#if !defined(_WIN32) + struct stat st; + stdoutFastPath = fstat(STDOUT_FASTPATH_FD, &st) == 0; +#else + std::cout << std::format("Ignoring {}\n", argv[i]); +#endif + } + else if (argv[i] == "--column-by-column"sv) + { + cout << std::format("Enabling column-by-column tests.\n"); + columnByColumn = true; + } else if (argv[i] == "--size"sv && i + 1 < argc) { ++i; - testSizeMB = static_cast(stoul(argv[i])); + testSizeMB = static_cast(std::stoul(argv[i])); } else if (argv[i] == "--help"sv || argv[i] == "-h"sv) { - cout << std::format("{} [--null-sink] [--size MB]\n", argv[0]); + cout << std::format("{} [--null-sink] [--fixed-size] [--stdout-fastpath] [--column-by-column] [--size MB] [--output FILE] [--help]\n", argv[0]); return EXIT_SUCCESS; } else if (argv[i] == "--output"sv && i + 1 < argc) @@ -135,10 +168,14 @@ int main(int argc, char const* argv[]) } } - contour::termbench::Benchmark tb { nullSink ? nullWrite : chunkedWriteToStdout, + auto const writer = nullSink ? nullWrite + : stdoutFastPath ? chunkedWriteToStdout + : chunkedWriteToStdout; + + contour::termbench::Benchmark tb { writer, testSizeMB, // MB per test - width, - height }; + requestedTerminalSize.columns, + requestedTerminalSize.lines }; // mlfgb tb.add(contour::termbench::tests::many_lines()); @@ -146,17 +183,22 @@ int main(int argc, char const* argv[]) tb.add(contour::termbench::tests::sgr_fg_lines()); tb.add(contour::termbench::tests::sgr_fgbg_lines()); tb.add(contour::termbench::tests::binary()); - // tb.add(contour::termbench::tests::binary()); - constexpr size_t Max_lines { 200 }; - for (size_t i = 0; i < Max_lines; ++i) - tb.add(contour::termbench::tests::ascii_line(i)); - for (size_t i = 0; i < Max_lines; ++i) - tb.add(contour::termbench::tests::sgr_line(i)); - for (size_t i = 0; i < Max_lines; ++i) - tb.add(contour::termbench::tests::sgrbg_line(i)); - - cout << "\033[8;30;100t"; - cout.flush(); + if (columnByColumn) + { + auto const maxColumns { requestedTerminalSize.columns * 2u }; + for (size_t i = 0; i < maxColumns; ++i) + tb.add(contour::termbench::tests::ascii_line(i)); + for (size_t i = 0; i < maxColumns; ++i) + tb.add(contour::termbench::tests::sgr_line(i)); + for (size_t i = 0; i < maxColumns; ++i) + tb.add(contour::termbench::tests::sgrbg_line(i)); + } + + if (requestedTerminalSize != initialTerminalSize) + { + cout << std::format("\033[8;{};{};t", requestedTerminalSize.lines, requestedTerminalSize.columns); + cout.flush(); + } tb.runAll();