Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ jobs:
working-directory: ctprintf
run: cmake --workflow --preset release-workflow

- name: Run performance benchmarks
working-directory: ctprintf
run: |
cmake --workflow --fresh --preset benchmark-workflow
./build/benchmark/benchmarks/ctprintf_benchmarks \
--benchmark_min_time=1s \
--benchmark_repetitions=3

- name: Build Debian package
run: ./scripts/build-package.sh

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ cmake --build build
ctest --test-dir build --output-on-failure
```

## Benchmarking

The optional benchmarks compare `ctprintf` with `std::snprintf` using the same
printf-style formats and fixed-size output buffers. They require Google
Benchmark:

```bash
cmake --workflow --preset benchmark-workflow
./ctprintf/build/benchmark/benchmarks/ctprintf_benchmarks
```

The benchmark target is separate from CTest because execution time varies
between machines and system load.

## Creating a package

CPack creates a gzip-compressed tarball containing the installable headers,
Expand Down
5 changes: 5 additions & 0 deletions ctprintf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

option(ENABLE_TESTING "Build and enable tests" OFF)
option(ENABLE_BENCHMARKS "Build performance benchmarks" OFF)

add_library(ctprintf INTERFACE)

Expand Down Expand Up @@ -62,6 +63,10 @@ if(ENABLE_TESTING)
add_subdirectory(tests)
endif() # ENABLE_TESTING

if(ENABLE_BENCHMARKS)
add_subdirectory(benchmarks)
endif() # ENABLE_BENCHMARKS

set(CPACK_GENERATOR "TGZ")
set(CPACK_PACKAGE_CONTACT "Thomas Willetal <t.willetal@googlemail.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Compile-time checked printf-style formatting library")
Expand Down
27 changes: 27 additions & 0 deletions ctprintf/CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@
"cacheVariables": {
"CMAKE_CXX_CLANG_TIDY": "clang-tidy;--warnings-as-errors=*"
}
},
{
"name": "benchmark",
"displayName": "Benchmark",
"inherits": "release",
"cacheVariables": {
"ENABLE_BENCHMARKS": "ON",
"ENABLE_TESTING": "OFF"
}
}
],
"buildPresets": [
Expand All @@ -49,6 +58,10 @@
{
"name": "clang-tidy",
"configurePreset": "clang-tidy"
},
{
"name": "benchmark",
"configurePreset": "benchmark"
}
],
"testPresets": [
Expand Down Expand Up @@ -135,6 +148,20 @@
"name": "clang-tidy"
}
]
},
{
"name": "benchmark-workflow",
"displayName": "Benchmark: configure and build",
"steps": [
{
"type": "configure",
"name": "benchmark"
},
{
"type": "build",
"name": "benchmark"
}
]
}
]
}
5 changes: 5 additions & 0 deletions ctprintf/benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
find_package(benchmark CONFIG REQUIRED)

add_executable(ctprintf_benchmarks formatting.cpp)

target_link_libraries(ctprintf_benchmarks PRIVATE ctprintf::ctprintf benchmark::benchmark)
86 changes: 86 additions & 0 deletions ctprintf/benchmarks/formatting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include <benchmark/benchmark.h>

#include <ctprintf/format.hpp>

#include <array>
#include <cstddef>
#include <cstdio>

namespace {

class Buffer {
public:
void clear() { size_ = 0; }

void put(char character) { characters_[size_++] = character; }

private:
std::array<char, 128> characters_{};
std::size_t size_ = 0;
};

void benchmark_ctprintf_hexadecimal(benchmark::State &state)
{
constexpr unsigned int value = 0xDEADBEEFU;
Buffer output;

for (auto _ : state) {
output.clear();
ctprintf::format(output, "value=%08x", value);
benchmark::DoNotOptimize(output);
benchmark::ClobberMemory();
}
}

void benchmark_snprintf_hexadecimal(benchmark::State &state)
{
constexpr unsigned int value = 0xDEADBEEFU;
std::array<char, 128> output{};

for (auto _ : state) {
int result = std::snprintf(output.data(), output.size(), "value=%08x", value);
benchmark::DoNotOptimize(result);
benchmark::DoNotOptimize(output);
benchmark::ClobberMemory();
}
}

void benchmark_ctprintf_mixed(benchmark::State &state)
{
constexpr unsigned int identifier = 0x42U;
constexpr int status = -17;
constexpr const char *name = "sensor";
Buffer output;

for (auto _ : state) {
output.clear();
ctprintf::format(output, "id=%08x status=%+d name=%s", identifier, status, name);
benchmark::DoNotOptimize(output);
benchmark::ClobberMemory();
}
}

void benchmark_snprintf_mixed(benchmark::State &state)
{
constexpr unsigned int identifier = 0x42U;
constexpr int status = -17;
constexpr const char *name = "sensor";
std::array<char, 128> output{};

for (auto _ : state) {
int result = std::snprintf(output.data(), output.size(), "id=%08x status=%+d name=%s",
identifier, status, name);
benchmark::DoNotOptimize(result);
benchmark::DoNotOptimize(output);
benchmark::ClobberMemory();
}
}

} // namespace

BENCHMARK(benchmark_ctprintf_hexadecimal);
BENCHMARK(benchmark_snprintf_hexadecimal);
BENCHMARK(benchmark_ctprintf_mixed);
BENCHMARK(benchmark_snprintf_mixed);

BENCHMARK_MAIN();
1 change: 1 addition & 0 deletions scripts/build-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommend
cmake \
debhelper \
devscripts \
libbenchmark-dev \
libgtest-dev \
ninja-build \
pipx
Expand Down
Loading