From 68b06f12d6b56048b945db07dc537de159ba49a5 Mon Sep 17 00:00:00 2001 From: Yaraslau Tamashevich Date: Fri, 15 Mar 2024 19:45:03 +0200 Subject: [PATCH] Update of termbench with scripts to create plots --- .clang-format | 81 +++++++ .github/mock-font-locator.yml | 10 + .github/workflows/build.yml | 119 +++++----- CMakeLists.txt | 3 - contour_results | 42 ++++ libtermbench/CMakeLists.txt | 1 - libtermbench/termbench.cpp | 436 ++++++++++++++++++---------------- libtermbench/termbench.h | 53 ++--- scripts/Xvfb-bench-run.sh | 30 +++ scripts/check-includes.sh | 14 ++ scripts/install-deps.sh | 9 - scripts/plot_results.jl | 93 ++++++++ scripts/xvfb-deps.sh | 66 +++++ tb/CMakeLists.txt | 2 +- tb/main.cpp | 142 ++++++----- 15 files changed, 748 insertions(+), 353 deletions(-) create mode 100644 .clang-format create mode 100644 .github/mock-font-locator.yml create mode 100644 contour_results create mode 100755 scripts/Xvfb-bench-run.sh create mode 100755 scripts/check-includes.sh create mode 100644 scripts/plot_results.jl create mode 100755 scripts/xvfb-deps.sh diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5edcb6b --- /dev/null +++ b/.clang-format @@ -0,0 +1,81 @@ +--- +BasedOnStyle: Microsoft +AccessModifierOffset: '-2' +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: 'true' +AlignConsecutiveDeclarations: 'false' +AlignEscapedNewlines: Left +AlignOperands: 'true' +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'true' +AllowAllConstructorInitializersOnNextLine: 'true' +AllowAllParametersOfDeclarationOnNextLine: 'true' +AllowShortBlocksOnASingleLine: 'false' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: 'false' +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: 'false' +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: 'false' +BinPackParameters: 'false' +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: 'true' +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: 'true' +ColumnLimit: '110' +CompactNamespaces: 'false' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' +ConstructorInitializerIndentWidth: '4' +ContinuationIndentWidth: '4' +Cpp11BracedListStyle: 'false' +DerivePointerAlignment: 'false' +FixNamespaceComments: 'true' +IncludeBlocks: Regroup +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: '4' +IndentWrappedFunctionNames: 'false' +Language: Cpp +MaxEmptyLinesToKeep: '1' +NamespaceIndentation: Inner +PenaltyBreakAssignment: '0' +PointerAlignment: Left +ReflowComments: 'true' +SortIncludes: 'true' +SortUsingDeclarations: 'true' +SpaceAfterCStyleCast: 'true' +SpaceAfterLogicalNot: 'false' +SpaceAfterTemplateKeyword: 'true' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeCpp11BracedList: 'true' +SpaceBeforeCtorInitializerColon: 'false' +SpaceBeforeInheritanceColon: 'false' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'false' +SpaceInEmptyParentheses: 'false' +SpacesInAngles: 'false' +SpacesInCStyleCastParentheses: 'false' +SpacesInContainerLiterals: 'false' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +Standard: Cpp11 +TabWidth: '4' +UseTab: Never +IncludeCategories: + - Regex: '^<(tb)/' + Priority: 0 + - Regex: '^<(libtermbench)/' + Priority: 1 + - Regex: '^' + Priority: 81 + - Regex: '<[[:alnum:]_]+\.h>' + Priority: 82 + - Regex: '.*' + Priority: 99 diff --git a/.github/mock-font-locator.yml b/.github/mock-font-locator.yml new file mode 100644 index 0000000..6c18003 --- /dev/null +++ b/.github/mock-font-locator.yml @@ -0,0 +1,10 @@ + +mock_font_locator: + # Ubuntu 20.04 + - { family: "monospace", slant: normal, weight: normal, path: "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" } + - { family: "emoji", slant: normal, weight: normal, path: "/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf" } + + # That would be it for macOS + # - { family: "monospace", slant: normal, weight: normal, path: "/Users/trapni/Library/Fonts/JetBrains Mono Regular Nerd Font Complete Mono.ttf" } + # - { family: "emoji", slant: normal, weight: normal, path: "/System/Library/Fonts/Apple Color Emoji.ttc" } + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 428b1b7..9ce85f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,26 @@ env: CTEST_OUTPUT_ON_FAILURE: 1 jobs: + + + check_clang_format: + name: "Check C++ style" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install clang + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 17 + sudo apt-get install clang-format-17 + - name: "Clang-format libtermbench" + run: find ./libtermbench -name "*.cpp" -o -name "*.h" | xargs clang-format-17 --Werror --dry-run + - name: "Clang-format tb1" + run: find ./tb -name "*.cpp" -o -name "*.h" | xargs clang-format-17 --Werror --dry-run + - name: "Check includes" + run: ./scripts/check-includes.sh + ubuntu_linux: name: "Ubuntu Linux 22.04" runs-on: ubuntu-22.04 @@ -43,62 +63,55 @@ jobs: set -ex sudo apt -q update sudo ./scripts/install-deps.sh + - name: "Install GCC 13" + run: sudo apt install g++-13 - name: "cmake" - run: cmake -S . -B build -DCMAKE_BUILD_TYPE="RelWithDebInfo" + run: cmake -S . -B build -DCMAKE_BUILD_TYPE="RelWithDebInfo" -D CMAKE_CXX_COMPILER="g++-13" - name: "build" run: cmake --build build/ -- -j3 - - osx: - name: "OS/X" - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: "**/cpm_modules" - key: ${{github.workflow}}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} - - name: set variables - id: set_vars - run: ./scripts/ci-set-vars.sh - env: - REPOSITORY: ${{ github.event.repository.name }} - - name: "Install dependencies" + - name: "install dependencies" + run: ./scripts/xvfb-deps.sh + - name: "Install contour" + run: | + wget https://github.com/contour-terminal/contour/releases/download/v0.4.3.6442/contour-0.4.3.6442-ubuntu22.04-amd64.deb + sudo dpkg -i contour-0.4.3.6442-ubuntu22.04-amd64.deb + - name: "create and patch contour.yml config file" run: | set -ex - #brew update - ./scripts/install-deps.sh - - name: "Create build directory" - run: mkdir build - - name: "Generate build files" - run: cmake -S . -B build -DCMAKE_BUILD_TYPE="RelWithDebInfo" - - name: "Build" - run: cmake --build build/ - - windows: - name: "Windows" - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - name: setup environment - shell: powershell - id: set_vars - run: .\scripts\ci-set-vars.ps1 - env: - REPOSITORY: ${{ github.event.repository.name }} - - name: "vcpkg: Install dependencies" - uses: lukka/run-vcpkg@v11.1 - id: runvcpkg + mkdir -p ~/.config/contour/ + contour generate config to ~/.config/contour/contour.yml + sed -i -e 's/locator: native/locator: mock/' ~/.config/contour/contour.yml + sed -i -e 's/strict_spacing: true/strict_spacing: false/' ~/.config/contour/contour.yml + cat .github/mock-font-locator.yml >> ~/.config/contour/contour.yml + cat ~/.config/contour/contour.yml + - name: "Install alacritty" + run: cargo install alacritty + - name: "Install kitty and xterm" + run: sudo apt install -y kitty xterm xvfb + - name: "run benchmarks" + run: ./scripts/Xvfb-bench-run.sh + - name: "ls" + run: ls -la + - name: "cat contour_results" + run: cat contour_results + - name: "cat kitty_results" + run: cat kitty_results + - name: "cat xterm_results" + run: cat xterm_results + - name: "cat alacritty_results" + run: cat alacritty_results + - name: "Set up Julia" + uses: julia-actions/setup-julia@v1 + - name: "Create Plots" + run: julia ./scripts/plot_results.jl + - name: "Upload results and plot" + uses: actions/upload-artifact@v3 with: - vcpkgDirectory: ${{ runner.workspace }}/vcpkg/ - vcpkgGitCommitId: 3e93bb69a1cadeb36fe9eca3b6f3912d84f618d5 - - name: "create build directory" - shell: powershell - run: | - If (!(Test-Path build)) - { - New-Item -ItemType Directory -Force -Path build - } - - name: "Generate build files" - run: cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="${{ runner.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -B build . - - name: "Build" - run: cmake --build build/ --config Release + name: benchmark_results + path: | + results_full.png + results_Ascii.png + contour_results + alacritty_results + xterm_results + kitty_results diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b40f21..4bf145c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,9 +28,6 @@ if(DEFINED MSVC) add_definitions(-D_USE_MATH_DEFINES) endif() -if(NOT TARGET fmt) - find_package(fmt REQUIRED) -endif() add_subdirectory(libtermbench) add_subdirectory(tb) diff --git a/contour_results b/contour_results new file mode 100644 index 0000000..0939aac --- /dev/null +++ b/contour_results @@ -0,0 +1,42 @@ +All 35 tests finished. +--------------------- + + many_lines: 0.0394 seconds, 81.218 MB/s (normalized: 27.723 KB/s) + long_lines: 1.0236 seconds, 25.890 MB/s (normalized: 8.837 KB/s) + sgr_fg_lines: 0.0342 seconds, 93.567 MB/s (normalized: 31.938 KB/s) + sgr_fg_bg_lines: 0.0388 seconds, 82.474 MB/s (normalized: 28.151 KB/s) + binary: 0.0415 seconds, 77.108 MB/s (normalized: 26.320 KB/s) + 0 chars per line: 5.0239 seconds, 6.108 MB/s (normalized: 2.085 KB/s) + 1 chars per line: 3.0519 seconds, 9.093 MB/s (normalized: 3.104 KB/s) + 2 chars per line: 2.0366 seconds, 13.525 MB/s (normalized: 4.617 KB/s) + 3 chars per line: 1.0785 seconds, 17.927 MB/s (normalized: 6.119 KB/s) + 4 chars per line: 1.0469 seconds, 21.784 MB/s (normalized: 7.435 KB/s) + 5 chars per line: 1.0299 seconds, 24.634 MB/s (normalized: 8.409 KB/s) + 6 chars per line: 1.0119 seconds, 28.597 MB/s (normalized: 9.761 KB/s) + 7 chars per line: 0.0981 seconds, 32.620 MB/s (normalized: 11.134 KB/s) + 8 chars per line: 0.0887 seconds, 36.077 MB/s (normalized: 12.314 KB/s) + 9 chars per line: 0.0816 seconds, 39.216 MB/s (normalized: 13.386 KB/s) + 0 chars with sgr per line: 0.0400 seconds, 80.000 MB/s (normalized: 27.307 KB/s) + 1 chars with sgr per line: 0.0430 seconds, 74.419 MB/s (normalized: 25.402 KB/s) + 2 chars with sgr per line: 0.0429 seconds, 74.592 MB/s (normalized: 25.461 KB/s) + 3 chars with sgr per line: 0.0410 seconds, 78.049 MB/s (normalized: 26.641 KB/s) + 4 chars with sgr per line: 0.0411 seconds, 77.859 MB/s (normalized: 26.576 KB/s) + 5 chars with sgr per line: 0.0404 seconds, 79.208 MB/s (normalized: 27.036 KB/s) + 6 chars with sgr per line: 0.0385 seconds, 83.117 MB/s (normalized: 28.371 KB/s) + 7 chars with sgr per line: 0.0384 seconds, 83.333 MB/s (normalized: 28.444 KB/s) + 8 chars with sgr per line: 0.0371 seconds, 86.253 MB/s (normalized: 29.441 KB/s) + 9 chars with sgr per line: 0.0363 seconds, 88.154 MB/s (normalized: 30.090 KB/s) + 0 chars with sgr and bg per line: 0.0347 seconds, 92.219 MB/s (normalized: 31.477 KB/s) + 1 chars with sgr and bg per line: 0.0368 seconds, 86.957 MB/s (normalized: 29.681 KB/s) + 2 chars with sgr and bg per line: 0.0355 seconds, 90.141 MB/s (normalized: 30.768 KB/s) + 3 chars with sgr and bg per line: 0.0366 seconds, 87.432 MB/s (normalized: 29.843 KB/s) + 4 chars with sgr and bg per line: 0.0345 seconds, 92.754 MB/s (normalized: 31.660 KB/s) + 5 chars with sgr and bg per line: 0.0339 seconds, 94.395 MB/s (normalized: 32.220 KB/s) + 6 chars with sgr and bg per line: 0.0342 seconds, 93.567 MB/s (normalized: 31.938 KB/s) + 7 chars with sgr and bg per line: 0.0377 seconds, 84.881 MB/s (normalized: 28.973 KB/s) + 8 chars with sgr and bg per line: 0.0358 seconds, 89.385 MB/s (normalized: 30.510 KB/s) + 9 chars with sgr and bg per line: 0.0380 seconds, 84.211 MB/s (normalized: 28.744 KB/s) + all tests: 29.0819 seconds, 37.560 MB/s (normalized: 12.820 KB/s) + + screen size: 100x30 + data size: 32.000 MB diff --git a/libtermbench/CMakeLists.txt b/libtermbench/CMakeLists.txt index 64caf4b..f34c81e 100644 --- a/libtermbench/CMakeLists.txt +++ b/libtermbench/CMakeLists.txt @@ -22,7 +22,6 @@ set_target_properties(termbench PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" ) -target_link_libraries(termbench PUBLIC fmt::fmt-header-only) target_include_directories(termbench PUBLIC $ $) diff --git a/libtermbench/termbench.cpp b/libtermbench/termbench.cpp index 5455f45..f8f37c2 100644 --- a/libtermbench/termbench.cpp +++ b/libtermbench/termbench.cpp @@ -14,14 +14,12 @@ #include "termbench.h" #include +#include +#include #include #include #include -#include - -#include - using std::function; using std::make_unique; using std::min; @@ -38,15 +36,15 @@ namespace { std::string sizeStr(double _value) { - if ((long double)(_value) >= (1024ull * 1024ull * 1024ull)) // GB - return fmt::format("{:7.3f} GB", _value / 1024.0 / 1024.0 / 1024.0); + if ((long double) (_value) >= (1024ull * 1024ull * 1024ull)) // GB + return std::format("{:7.3f} GB", _value / 1024.0 / 1024.0 / 1024.0); if (_value >= (1024 * 1024)) // MB - return fmt::format("{:7.3f} MB", _value / 1024.0 / 1024.0); + return std::format("{:7.3f} MB", _value / 1024.0 / 1024.0); if (_value >= 1024) // KB - return fmt::format("{:7.3f} KB", _value / 1024.0); - return fmt::format("{:7.3f} bytes", _value); + return std::format("{:7.3f} KB", _value / 1024.0); + return std::format("{:7.3f} bytes", _value); } -} +} // namespace using u16 = unsigned short; @@ -55,11 +53,11 @@ Benchmark::Benchmark(function _writer, unsigned short _width, unsigned short _height, function _beforeTest): - writer_{std::move(_writer)}, - beforeTest_{std::move(_beforeTest)}, - testSizeMB_{_testSizeMB}, - width_{_width}, - height_{_height} + writer_ { std::move(_writer) }, + beforeTest_ { std::move(_beforeTest) }, + testSizeMB_ { _testSizeMB }, + width_ { _width }, + height_ { _height } { } @@ -92,7 +90,7 @@ void Benchmark::runAll() } auto const diff = duration_cast(steady_clock::now() - beginTime); - results_.emplace_back(Result{*test, diff, totalSizeBytes}); + results_.emplace_back(Result { *test, diff, totalSizeBytes }); buffer->clear(); test->teardown(*buffer); @@ -103,14 +101,14 @@ void Benchmark::runAll() buffer->clear(); } } - +// void Benchmark::summarize(std::ostream& os) { - os << fmt::format("All {} tests finished.\n", results_.size()); - os << fmt::format("---------------------\n\n"); + os << std::format("All {} tests finished.\n", results_.size()); + os << std::format("---------------------\n\n"); auto const gridCellCount = width_ * height_; - std::chrono::milliseconds totalTime{}; + std::chrono::milliseconds totalTime {}; size_t totalBytes = 0; for (auto const& result: results_) { @@ -118,31 +116,27 @@ void Benchmark::summarize(std::ostream& os) auto const bps = double(result.bytesWritten) / (double(result.time.count()) / 1000.0); totalBytes += result.bytesWritten; totalTime += result.time; - os << fmt::format( - "{:>20}: {:>3}.{:04} seconds, {}/s (normalized: {}/s)\n", - test.name, - result.time.count() / 1000, - result.time.count() % 1000, - sizeStr(bps), - sizeStr(bps / static_cast(gridCellCount)) - ); + os << std::format("{:>40}: {:>3}.{:04} seconds, {}/s (normalized: {}/s)\n", + test.name, + result.time.count() / 1000, + result.time.count() % 1000, + sizeStr(bps), + sizeStr(bps / static_cast(gridCellCount))); } auto const bps = double(totalBytes) / (double(totalTime.count()) / 1000.0); - os << fmt::format( - "{:>20}: {:>3}.{:04} seconds, {}/s (normalized: {}/s)\n", - "all tests", - totalTime.count() / 1000, - totalTime.count() % 1000, - sizeStr(bps), - sizeStr(bps / static_cast(gridCellCount)) - ); + os << std::format("{:>40}: {:>3}.{:04} seconds, {}/s (normalized: {}/s)\n", + "all tests", + totalTime.count() / 1000, + totalTime.count() % 1000, + sizeStr(bps), + sizeStr(bps / static_cast(gridCellCount))); os << "\n"; - os << fmt::format(" screen size: {}x{}\n", width_, height_); - os << fmt::format(" data size: {}\n", sizeStr(static_cast(testSizeMB_ * 1024 * 1024))); + os << std::format(" screen size: {}x{}\n", width_, height_); + os << std::format(" data size: {}\n", sizeStr(static_cast(testSizeMB_ * 1024 * 1024))); } -} +} // namespace contour::termbench namespace contour::termbench::tests { @@ -150,216 +144,231 @@ namespace contour::termbench::tests namespace { -static char randomAsciiChar() -{ - auto constexpr Min = 'a'; // 0x20; - auto constexpr Max = 'z'; // 0x7E; - return static_cast(Min + rand() % (Max - Min + 1)); -} - -void writeChar(Buffer& _sink, char ch) -{ - _sink.write(std::string_view{&ch, 1}); -} - -void writeNumber(Buffer& _sink, unsigned _value) -{ - unsigned remains = _value; - for (unsigned divisor = 1000000000; divisor != 0; divisor /= 10) + static char randomAsciiChar() { - auto const digit = remains / divisor; - remains -= digit * divisor; - - if (digit || (_value != remains) || (divisor == 1)) - writeChar(_sink, static_cast('0' + digit)); + auto constexpr Min = 'a'; // 0x20; + auto constexpr Max = 'z'; // 0x7E; + return static_cast(Min + rand() % (Max - Min + 1)); } -} -void moveCursor(Buffer& _sink, unsigned x, unsigned y) -{ - writeChar(_sink, '\033'); - writeChar(_sink, '['); - writeNumber(_sink, y); - writeChar(_sink, ';'); - writeNumber(_sink, x); - writeChar(_sink, 'H'); -} - -void setTextColor(Buffer& _sink, uint8_t r, uint8_t g, uint8_t b) -{ - _sink.write("\033[38;2;"); - writeNumber(_sink, r & 0xFF); - writeChar(_sink, ';'); - writeNumber(_sink, g & 0xFF); - writeChar(_sink, ';'); - writeNumber(_sink, b & 0xFF); - writeChar(_sink, 'm'); -} - -void setBackgroundColor(Buffer& _sink, uint8_t r, uint8_t g, uint8_t b) -{ - _sink.write("\033[48;2;"); - writeNumber(_sink, r & 0xFF); - writeChar(_sink, ';'); - writeNumber(_sink, g & 0xFF); - writeChar(_sink, ';'); - writeNumber(_sink, b & 0xFF); - writeChar(_sink, 'm'); -} - -class ManyLines: public Test -{ -public: - ManyLines() noexcept: Test("many_lines", "") {} + void writeChar(Buffer& _sink, char ch) + { + _sink.write(std::string_view { &ch, 1 }); + } - void setup(unsigned short, unsigned short) override + void writeNumber(Buffer& _sink, unsigned _value) { - text.resize(4 * 1024 * 1024); - for (auto i = text.data(), e = i + text.size(); i != e; ++i) + unsigned remains = _value; + for (unsigned divisor = 1000000000; divisor != 0; divisor /= 10) { - char const value = randomAsciiChar(); - if (value % 26 != 0) - *i = value; - else - *i = '\n'; + auto const digit = remains / divisor; + remains -= digit * divisor; + + if (digit || (_value != remains) || (divisor == 1)) + writeChar(_sink, static_cast('0' + digit)); } } - void run(Buffer& _sink) noexcept override + void moveCursor(Buffer& _sink, unsigned x, unsigned y) { - while (_sink.good()) - _sink.write(text); + writeChar(_sink, '\033'); + writeChar(_sink, '['); + writeNumber(_sink, y); + writeChar(_sink, ';'); + writeNumber(_sink, x); + writeChar(_sink, 'H'); } -private: - std::string text; -}; + void setTextColor(Buffer& _sink, uint8_t r, uint8_t g, uint8_t b) + { + _sink.write("\033[38;2;"); + writeNumber(_sink, r & 0xFF); + writeChar(_sink, ';'); + writeNumber(_sink, g & 0xFF); + writeChar(_sink, ';'); + writeNumber(_sink, b & 0xFF); + writeChar(_sink, 'm'); + } -class LongLines: public Test -{ -public: - LongLines() noexcept: Test("long_lines", "") {} + void setBackgroundColor(Buffer& _sink, uint8_t r, uint8_t g, uint8_t b) + { + _sink.write("\033[48;2;"); + writeNumber(_sink, r & 0xFF); + writeChar(_sink, ';'); + writeNumber(_sink, g & 0xFF); + writeChar(_sink, ';'); + writeNumber(_sink, b & 0xFF); + writeChar(_sink, 'm'); + } - void run(Buffer& _sink) noexcept override + class ManyLines: public Test { - while (_sink.good()) + public: + ManyLines() noexcept: Test("many_lines", "") {} + + void setup(unsigned short, unsigned short) override { - writeChar(_sink, randomAsciiChar()); + text.resize(4 * 1024 * 1024); + for (auto i = text.data(), e = i + text.size(); i != e; ++i) + { + char const value = randomAsciiChar(); + if (value % 26 != 0) + *i = value; + else + *i = '\n'; + } } - } -}; -class SgrFgColoredText: public Test -{ -public: - SgrFgColoredText() noexcept: Test("sgr_fg_lines", "") {} + void run(Buffer& _sink) noexcept override + { + while (_sink.good()) + _sink.write(text); + } - unsigned short width = 80; - unsigned short height = 24; + private: + std::string text; + }; - void setup(unsigned short _width, unsigned short _height) noexcept override + class LongLines: public Test { - width = _width; - height = _height; - } + public: + LongLines() noexcept: Test("long_lines", "") {} + + void run(Buffer& _sink) noexcept override + { + while (_sink.good()) + { + writeChar(_sink, randomAsciiChar()); + } + } + }; - void run(Buffer& _sink) noexcept override + class SgrFgColoredText: public Test { - for (unsigned frameID = 0; _sink.good(); ++frameID) + public: + SgrFgColoredText() noexcept: Test("sgr_fg_lines", "") {} + + unsigned short width = 80; + unsigned short height = 24; + + void setup(unsigned short _width, unsigned short _height) noexcept override + { + width = _width; + height = _height; + } + + void run(Buffer& _sink) noexcept override { - for (u16 y = 0; y < height; ++y) + for (unsigned frameID = 0; _sink.good(); ++frameID) { - moveCursor(_sink, 1, y + 1u); - for (u16 x = 0; x < width; ++x) + for (u16 y = 0; y < height; ++y) { - 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('a' + (frameID + x + y) % ('z' - 'a'))); + moveCursor(_sink, 1, y + 1u); + for (u16 x = 0; x < width; ++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('a' + (frameID + x + y) % ('z' - 'a'))); + } } } } - } -}; + }; -class SgrFgBgColoredText: public Test -{ -public: - SgrFgBgColoredText() noexcept: Test("sgr_fg_bg_lines", "") {} + class SgrFgBgColoredText: public Test + { + public: + SgrFgBgColoredText() noexcept: Test("sgr_fg_bg_lines", "") {} - unsigned short width = 80; - unsigned short height = 24; + unsigned short width = 80; + unsigned short height = 24; - void setup(unsigned short _width, unsigned short _height) noexcept override - { - width = _width; - height = _height; - } + void setup(unsigned short _width, unsigned short _height) noexcept override + { + width = _width; + height = _height; + } - void run(Buffer& _sink) noexcept override - { - for (unsigned frameID = 0; _sink.good(); ++frameID) + void run(Buffer& _sink) noexcept override { - for (u16 y = 0; y < height; ++y) + for (unsigned frameID = 0; _sink.good(); ++frameID) { - moveCursor(_sink, 1, y + 1u); - for (u16 x = 0; x < width; ++x) + for (u16 y = 0; y < height; ++y) { - auto r = static_cast(frameID); - auto g = static_cast(frameID + y); - auto b = static_cast(frameID + y + x); - setTextColor(_sink, r, g, b); - - r = static_cast(frameID + y + x); - g = static_cast(frameID + y); - b = static_cast(frameID); - setBackgroundColor(_sink, r, g, b); - - writeChar(_sink, static_cast('a' + (frameID + x + y) % ('z' - 'a'))); + moveCursor(_sink, 1, y + 1u); + for (u16 x = 0; x < width; ++x) + { + auto r = static_cast(frameID); + auto g = static_cast(frameID + y); + auto b = static_cast(frameID + y + x); + setTextColor(_sink, r, g, b); + + r = static_cast(frameID + y + x); + g = static_cast(frameID + y); + b = static_cast(frameID); + setBackgroundColor(_sink, r, g, b); + + writeChar(_sink, static_cast('a' + (frameID + x + y) % ('z' - 'a'))); + } } } } - } -}; - -class Binary: public Test -{ -public: - Binary() noexcept: Test("binary", "") {} + }; - void setup(unsigned short, unsigned short) override + class Binary: public Test { - text.resize(4 * 1024 * 1024); - for (auto i = text.data(), e = i + text.size(); i != e; ++i) + public: + Binary() noexcept: Test("binary", "") {} + + void setup(unsigned short, unsigned short) override { - char const value = randomAsciiChar(); - if (value % 26 != 0) - *i = value; - else - *i = '\n'; + text.resize(4 * 1024 * 1024); + for (auto i = text.data(), e = i + text.size(); i != e; ++i) + { + char const value = randomAsciiChar(); + if (value % 26 != 0) + *i = value; + else + *i = '\n'; + } } - } - void run(Buffer& _sink) noexcept override - { - while (_sink.good()) + void run(Buffer& _sink) noexcept override { - _sink.write(text); + while (_sink.good()) + { + _sink.write(text); + } } - } - void teardown(Buffer& _sink) noexcept override + void teardown(Buffer& _sink) noexcept override { _sink.write("\033c"); } + + private: + std::string text; + }; + + class Line: public Test { - _sink.write("\033c"); - } + public: + Line(std::string name, std::string text): Test(name, ""), text { text } {} + void setup(unsigned short, unsigned short) override {} -private: - std::string text; -}; + void run(Buffer& _sink) noexcept override + { + for (int i = 0; i < 10000; ++i) + { + while (_sink.good()) + _sink.write(text); + } + } -} + private: + std::string text; + }; +} // namespace unique_ptr many_lines() { @@ -386,4 +395,33 @@ unique_ptr binary() return make_unique(); } +unique_ptr ascii_line(size_t N) +{ + auto name = std::to_string(N) + " chars per line"; + auto text = std::string(N, 'a') + std::string { "\n" }; + return make_unique(name, text); } + +unique_ptr sgr_line(size_t N) +{ + auto name = std::to_string(N) + " chars with sgr per line"; + std::string text {}; + text += std::string { "\033[38;2;20;200;200m" }; + text += std::string(N, 'a'); + text += std::string { "\n" }; + text += std::string { "\033[38;2;255;255;255m" }; + return make_unique(name, text); +} + +unique_ptr sgrbg_line(size_t N) +{ + auto name = std::to_string(N) + " chars with sgr and bg per line"; + std::string text {}; + text += std::string { "\033[38;2;20;200;200m\033[48;2;100;100;100m" }; + text += std::string(N, 'a'); + text += std::string { "\033[38;2;255;255;255m\033[48;2;0;0;0m" }; + text += std::string { "\n" }; + return make_unique(name, text); +} + +} // namespace contour::termbench::tests diff --git a/libtermbench/termbench.h b/libtermbench/termbench.h index ec0f64c..b613480 100644 --- a/libtermbench/termbench.h +++ b/libtermbench/termbench.h @@ -27,21 +27,14 @@ namespace contour::termbench struct Buffer { -public: - explicit Buffer(size_t _maxWriteSizeMB) noexcept: - maxWriteSize{_maxWriteSizeMB * 1024 * 1024} - {} + public: + explicit Buffer(size_t _maxWriteSizeMB) noexcept: maxWriteSize { _maxWriteSizeMB * 1024 * 1024 } {} - bool good() const noexcept - { - return nwritten < maxWriteSize; - } + bool good() const noexcept { return nwritten < maxWriteSize; } void write(std::string_view _data) noexcept { - auto const n = nwritten + _data.size() < maxWriteSize - ? _data.size() - : maxWriteSize - nwritten; + 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; @@ -51,30 +44,29 @@ struct Buffer nwritten += n; } - 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(), nwritten }; } void clear() noexcept { nwritten = 0; } bool empty() const noexcept { return nwritten == 0; } -private: + private: size_t maxWriteSize = 4 * 1024 * 1024; - std::array data{}; + std::array data {}; size_t nwritten = 0; }; /// Describes a single test. struct Test { - std::string_view name; - std::string_view description; + std::string name; + std::string description; virtual ~Test() = default; - Test(std::string_view _name, std::string_view _description) noexcept: - name{_name}, - description{_description} - {} + Test(std::string _name, std::string _description) noexcept: name { _name }, description { _description } + { + } virtual void setup(unsigned short, unsigned short) {} virtual void run(Buffer&) noexcept = 0; @@ -90,7 +82,7 @@ struct Result class Benchmark { -public: + public: Benchmark(std::function _writer, size_t _testSizeMB, unsigned short _width, @@ -105,7 +97,7 @@ class Benchmark std::vector const& results() const noexcept { return results_; } -private: + private: std::function writer_; std::function beforeTest_; size_t testSizeMB_; @@ -116,14 +108,17 @@ class Benchmark std::vector results_; }; -} +} // namespace contour::termbench // Holds a set of pre-defined terminal benchmark tests. namespace contour::termbench::tests { - std::unique_ptr many_lines(); - std::unique_ptr long_lines(); - std::unique_ptr sgr_fg_lines(); - std::unique_ptr sgr_fgbg_lines(); - std::unique_ptr binary(); -} +std::unique_ptr many_lines(); +std::unique_ptr long_lines(); +std::unique_ptr sgr_fg_lines(); +std::unique_ptr sgr_fgbg_lines(); +std::unique_ptr binary(); +std::unique_ptr ascii_line(size_t); +std::unique_ptr sgr_line(size_t); +std::unique_ptr sgrbg_line(size_t); +} // namespace contour::termbench::tests diff --git a/scripts/Xvfb-bench-run.sh b/scripts/Xvfb-bench-run.sh new file mode 100755 index 0000000..a90d637 --- /dev/null +++ b/scripts/Xvfb-bench-run.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# +# Usage: Xvfb-contour-run.sh + +set -x + + +LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-true}" +DISPLAY=:99 +export LIBGL_ALWAYS_SOFTWARE +export DISPLAY + +Xvfb $DISPLAY -screen 0 1280x1024x24 & +XVFB_PID=$! +trap "kill $XVFB_PID" EXIT + +sleep 3 + +TB_BIN=$PWD/build/tb/tb + +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 diff --git a/scripts/check-includes.sh b/scripts/check-includes.sh new file mode 100755 index 0000000..97d5ab1 --- /dev/null +++ b/scripts/check-includes.sh @@ -0,0 +1,14 @@ +#! /bin/bash + +grep -R --include '*.cpp' -E '^#.*include ".*"$' src/ +rv1=$? + +grep -R --include '*.h' -E '^#.*include ".*"$' src/ +rv2=$? + +if [[ $rv1 -eq 0 || $rv2 -eq 0 ]]; then + echo 1>&2 "Error: found #include \"...\" in C++ files." + exit 1 +else + echo "All good. ;-)" +fi diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh index 3fcb4c6..16dc616 100755 --- a/scripts/install-deps.sh +++ b/scripts/install-deps.sh @@ -11,7 +11,6 @@ install_deps_ubuntu() cmake g++ make - libfmt-dev ) if [[ "${RELEASE}" < "19.04" ]]; then @@ -34,13 +33,6 @@ main_linux() ;; esac } - -main_darwin() -{ - # brew install cmake clang? - brew install fmt -} - main() { case "$OSTYPE" in @@ -48,7 +40,6 @@ main() main_linux ;; darwin*) - main_darwin ;; *) echo "OS not supported." diff --git a/scripts/plot_results.jl b/scripts/plot_results.jl new file mode 100644 index 0000000..e77ecd3 --- /dev/null +++ b/scripts/plot_results.jl @@ -0,0 +1,93 @@ +using Pkg +Pkg.add("CairoMakie") +using CairoMakie + +function parse_line(line) + if count("MB",line) > 0 + return parse(Float64,strip(split(split(line,',')[2],"MB/s")[1])) + else # KB + return parse(Float64,split(split(split(line,",")[2], "(")[1]," ")[2]) / 1024 + end +end + +function insert_from_data(ax, file_name, r,g,b) + data = split(open(io->read(io, String), file_name), '\n') + terminal_name = split(file_name,"_")[1] + + ascii_lines = [] + sgr_lines = [] + sgr_bg_lines = [] + for el in data + if occursin("chars per line", el) + push!(ascii_lines, el) + end + if occursin("chars with sgr per line", el) + push!(sgr_lines, el) + end + if occursin("chars with sgr and bg per line", el) + push!(sgr_bg_lines, el) + end + end + + ascii_speed = [ parse_line(val) for val in ascii_lines] + sgr_speed = [ parse_line(val) for val in sgr_lines] + sgr_bg_speed = [ parse_line(val) for val in sgr_bg_lines] + + lines!(ax,ascii_speed, label= terminal_name * "_ascii", color = RGBf(r, g, b)) + lines!(ax,sgr_speed, label=terminal_name*"_sgr", color = RGBf(r * 0.7 , g* 0.7 , b* 0.7 )) + lines!(ax,sgr_bg_speed, label=terminal_name*"_sgr_and_bg", color = RGBf(r * 0.4 , g* 0.4 , b* 0.4 )) + +end + +function insert_from_data_ascii(ax, file_name) + data = split(open(io->read(io, String), file_name), '\n') + terminal_name = split(file_name,"_")[1] + + ascii_lines = [] + sgr_lines = [] + sgr_bg_lines = [] + for el in data + if occursin("chars per line", el) + push!(ascii_lines, el) + end + if occursin("chars with sgr per line", el) + push!(sgr_lines, el) + end + if occursin("chars with sgr and bg per line", el) + push!(sgr_bg_lines, el) + end + end + + ascii_speed = [ parse_line(val) for val in ascii_lines] + + lines!(ax,ascii_speed, label= terminal_name * "_ascii") +end + + + +fig = Figure() +ax = Axis(fig[1,1], xlabel = "Length of line", ylabel = "throughput, MB/s") +ls = [] + +insert_from_data(ax,"contour_results", 1.0, 0.5 ,0.5) +insert_from_data(ax,"alacritty_results", 0.5, 1.0, 0.5) +insert_from_data(ax,"xterm_results", 0.5, 0.5, 1.0) +insert_from_data(ax,"kitty_results", 0.0, 0.8, 1.0) + +fig[1, 2] = Legend(fig, ax,framevisible = false) + +save("results_full.png", fig) + + + +fig = Figure() +ax = Axis(fig[1,1], xlabel = "Length of line", ylabel = "throughput, MB/s") +ls = [] + +insert_from_data_ascii(ax,"contour_results") +insert_from_data_ascii(ax,"alacritty_results") +insert_from_data_ascii(ax,"xterm_results") +insert_from_data_ascii(ax,"kitty_results") +fig[1, 2] = Legend(fig, ax,framevisible = false) + +save("results_ascii.png", fig) diff --git a/scripts/xvfb-deps.sh b/scripts/xvfb-deps.sh new file mode 100755 index 0000000..45ca102 --- /dev/null +++ b/scripts/xvfb-deps.sh @@ -0,0 +1,66 @@ +#! /bin/bash +set -ex + +packages=" + libqt6core5compat6 \ + libqt6gui6 \ + libqt6multimedia6 \ + libqt6multimediaquick6 \ + libqt6multimediawidgets6 \ + libqt6opengl6 \ + libqt6openglwidgets6 \ + libqt6qml6 \ + libqt6quick6 \ + \ + qml6-module-qt-labs-platform \ + qml6-module-qt5compat-graphicaleffects \ + qml6-module-qtmultimedia \ + qml6-module-qtqml-workerscript \ + qml6-module-qtquick-controls \ + qml6-module-qtquick-layouts \ + qml6-module-qtquick-templates \ + qml6-module-qtquick-window \ + qt6-qpa-plugins \ + \ + xvfb \ + \ + ffmpeg \ + libavcodec58 \ + libavdevice58 \ + libavformat58 \ + libavutil56 \ + libdeflate0 \ + libncurses6 \ + libqrcodegen1 \ + libswscale5 \ + libunistring2 \ + \ + libfontconfig1 \ + libfreetype6 \ + libharfbuzz0b \ + \ + libqt5core5a \ + libqt5gui5 \ + libqt5gui5 \ + libqt5multimedia5 \ + libqt5multimedia5-plugins \ + libqt5network5 \ + libqt5opengl5-dev \ + libqt5x11extras5 \ + libqt5x11extras5-dev \ + qml-module-qt-labs-platform \ + qml-module-qtmultimedia \ + qml-module-qtquick-controls \ + qml-module-qtquick-controls2 \ + qtbase5-dev \ + qtdeclarative5-dev \ + qtmultimedia5-dev \ + qtquickcontrols2-5-dev \ + \ + libutempter0 \ + libyaml-cpp0.7\ + \ + cargo +" + +sudo apt install -y $packages diff --git a/tb/CMakeLists.txt b/tb/CMakeLists.txt index 08f91d4..62a5ffa 100644 --- a/tb/CMakeLists.txt +++ b/tb/CMakeLists.txt @@ -1,7 +1,7 @@ include(GNUInstallDirs) add_executable(tb main.cpp) -target_link_libraries(tb PRIVATE termbench fmt::fmt-header-only) +target_link_libraries(tb PRIVATE termbench) # Set the RPATH so that the executable can find the shared libraries # when installed in a non-standard location diff --git a/tb/main.cpp b/tb/main.cpp index d576993..b65e6c5 100644 --- a/tb/main.cpp +++ b/tb/main.cpp @@ -13,11 +13,13 @@ */ #include -#include + #include #include +#include +#include +#include #include -#include using std::cerr; using std::cout; @@ -28,85 +30,87 @@ using namespace std::string_view_literals; using namespace std::placeholders; #if !defined(_WIN32) -#include -#include + #include + + #include #else -#include + #include #endif namespace { - std::pair getTerminalSize() noexcept - { - auto const DefaultSize = std::pair{80, 24}; +std::pair getTerminalSize() noexcept +{ + auto const DefaultSize = std::pair { 80, 24 }; #if !defined(_WIN32) - winsize ws; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) - return DefaultSize; - return {ws.ws_col, ws.ws_row}; -#else - // TODO: Windows + winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) return DefaultSize; + return { ws.ws_col, ws.ws_row }; +#else + // TODO: Windows + return DefaultSize; #endif - } - - void nullWrite(char const*, size_t) - { - } +} - void chunkedWriteToStdout(char const* _data, size_t _size) - { - auto constexpr PageSize = 4096; // 8192; +void nullWrite(char const*, size_t) +{ +} - #if defined(_WIN32) - HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD nwritten{}; - #endif +void chunkedWriteToStdout(char const* _data, size_t _size) +{ + auto constexpr PageSize = 4096; // 8192; - while (_size >= PageSize) - { - #if !defined(_WIN32) - auto const n = write(STDOUT_FILENO, _data, PageSize); - if (n < 0) - perror("write"); - _data += n; - _size -= static_cast(n); - #else - WriteConsoleA(stdoutHandle, _data, static_cast(_size), &nwritten, nullptr); - #endif - } +#if defined(_WIN32) + HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD nwritten {}; +#endif - #if !defined(_WIN32) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-result" - write(STDOUT_FILENO, _data, _size); - #pragma GCC diagnostic pop - #else + while (_size >= PageSize) + { +#if !defined(_WIN32) + auto const n = write(STDOUT_FILENO, _data, PageSize); + if (n < 0) + perror("write"); + _data += n; + _size -= static_cast(n); +#else WriteConsoleA(stdoutHandle, _data, static_cast(_size), &nwritten, nullptr); - #endif +#endif } + +#if !defined(_WIN32) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-result" + write(STDOUT_FILENO, _data, _size); + #pragma GCC diagnostic pop +#else + WriteConsoleA(stdoutHandle, _data, static_cast(_size), &nwritten, nullptr); +#endif } +} // namespace int main(int argc, char const* argv[]) { - #if defined(_WIN32) +#if defined(_WIN32) { HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleMode(stdoutHandle, ENABLE_VIRTUAL_TERMINAL_PROCESSING); } - #endif +#endif // TODO: Also run against NullSink to get a base value. auto [width, height] = getTerminalSize(); size_t testSizeMB = 32; bool nullSink = false; + std::string fileout {}; for (int i = 1; i < argc; ++i) { if (argv[i] == "--null-sink"sv) { - cout << fmt::format("Using null-sink.\n"); + cout << std::format("Using null-sink.\n"); nullSink = true; } else if (argv[i] == "--size"sv && i + 1 < argc) @@ -116,35 +120,57 @@ int main(int argc, char const* argv[]) } else if (argv[i] == "--help"sv || argv[i] == "-h"sv) { - cout << fmt::format("{} [--null-sink] [--size MB]\n", argv[0]); + cout << std::format("{} [--null-sink] [--size MB]\n", argv[0]); return EXIT_SUCCESS; } + else if (argv[i] == "--output"sv && i + 1 < argc) + { + ++i; + fileout = argv[i]; + } else { - cerr << fmt::format("Invalid argument usage.\n"); + cerr << std::format("Invalid argument usage.\n"); return EXIT_FAILURE; } } - contour::termbench::Benchmark tb{ - nullSink ? nullWrite : chunkedWriteToStdout, - testSizeMB, // MB per test - width, - height - }; + contour::termbench::Benchmark tb { nullSink ? nullWrite : chunkedWriteToStdout, + testSizeMB, // MB per test + width, + height }; // mlfgb tb.add(contour::termbench::tests::many_lines()); tb.add(contour::termbench::tests::long_lines()); 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()); + // 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(); tb.runAll(); cout << "\033[m\033[H\033[J"; cout.flush(); - tb.summarize(cout); + if (fileout.empty()) + tb.summarize(cout); + else + { + cout << "Writing summary into " << fileout << std::endl; + std::ofstream writerToFile; + writerToFile.open(fileout); + tb.summarize(writerToFile); + } return EXIT_SUCCESS; }