diff --git a/CMakeLists.txt b/CMakeLists.txt
index a725b3c..3589bcd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.20.0) # RHEL 8
project(benchmarks
- VERSION 0.0.0.1 # PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH, and PROJECT_VERSION_TWEAK.
+ VERSION 0.0.1.0 # PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH, and PROJECT_VERSION_TWEAK.
DESCRIPTION "Various benchmarks"
HOMEPAGE_URL "https://github.com/gunchev/bench/"
LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
-set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# set(CMAKE_VERBOSE_MAKEFILE TRUE) # or "cd build; make VERBOSE=1"
@@ -18,6 +18,9 @@ if(NOT CMAKE_BUILD_TYPE)
"Choose the type of build, options are: Debug DebugCov RelWithDebInfo Release MinSizeRel." FORCE)
endif()
+include(FetchContent)
+
+
# SOVERSION is ignored if NO_SONAME property is set.
# "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK}"
set(${PROJECT_NAME}_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
@@ -47,19 +50,14 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Compiler/linker flags
-if(EXISTS "/etc/fedora-release")
-set(CMAKE_C_FLAGS "-Wall -Wextra")
-set(CMAKE_CXX_FLAGS "-Wall -Wextra")
-else()
set(CMAKE_C_FLAGS "-Wall -Wextra -Werror")
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror")
-endif()
+
+# https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html#Template-Instantiation
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
-# https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html#Template-Instantiation
-
set(CMAKE_REQUIRED_QUIET_OLD ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET ON)
macro(CheckAndAddFlag flag)
@@ -120,20 +118,24 @@ set(CMAKE_C_FLAGS_MINSIZEREL "-Os -ggdb3 -D_FORTIFY_SOURCE=2 -DN
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -ggdb3 -D_FORTIFY_SOURCE=2 -DNDEBUG")
+# Google benchmark
+find_package(benchmark 1.9.4)
+if (NOT benchmark_FOUND)
+ message(STATUS "Google Benchmark not found, fetching it...")
-# Enable testing?
-include(FindPkgConfig)
-pkg_check_modules(CPPUNIT cppunit>=1.14.0) # REQUIRED
-if(CPPUNIT_FOUND)
- enable_testing()
- include(CTest)
- find_program(CTEST_COVERAGE_COMMAND NAMES gcov)
- find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind)
- add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
+ FetchContent_Declare(
+ benchmark
+ GIT_REPOSITORY https://github.com/google/benchmark.git
+ GIT_TAG v1.9.4
+ )
+
+ # Disable benchmark's own tests
+ set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
+ set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE)
+
+ FetchContent_MakeAvailable(benchmark)
endif()
-
-
-find_package(benchmark REQUIRED)
+message(STATUS "Found Google benchmark version: ${benchmark_VERSION}")
# Threads library
# set(THREADS_PREFER_PTHREAD_FLAG TRUE)
@@ -145,8 +147,29 @@ find_package(benchmark REQUIRED)
# pkg_check_modules(HIREDIS REQUIRED hiredis>=1.0.2)
+# Enable testing?
+#include(FindPkgConfig)
+#pkg_check_modules(CPPUNIT cppunit>=1.14.0) # REQUIRED
+# set(GTEST_MIN_VERSION 1.13.0)
+# find_package(GTest ${GTEST_MIN_VERSION} QUIET)
+# if (NOT GTest_FOUND)
+# message(STATUS "GoogleTest not found, fetching it...")
+# FetchContent_Declare(
+# googletest
+# GIT_REPOSITORY https://github.com/google/googletest.git
+# GIT_TAG v1.14.0
+# )
+# # Don't install or build gtest's own tests
+# set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
+# set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
+
+# FetchContent_MakeAvailable(googletest)
+# endif()
+
+
# include_directories(src)
add_executable(transform src/transform.cpp)
target_link_libraries(transform PRIVATE benchmark::benchmark) # fmt::fmt Threads::Threads)
+target_compile_features(transform PRIVATE cxx_std_17)
install(TARGETS transform DESTINATION bin)
diff --git a/README.md b/README.md
index 5c4d00d..4108928 100644
--- a/README.md
+++ b/README.md
@@ -4,4 +4,59 @@
Various C++ benchmarks:
- transform.cpp - benchmark std::transform vs loop and custom lowercase and tolower
-
+
+---
+```shell
+ $ make releasec # clang++ 21.1.8
+ $ ./build/releasec/transform
+```
+
+ 2026-02-07T15:43:00+02:00
+ Running ./build/releasec/transform
+ Run on (32 X 5187.5 MHz CPU s)
+ CPU Caches:
+ L1 Data 48 KiB (x16)
+ L1 Instruction 32 KiB (x16)
+ L2 Unified 1024 KiB (x16)
+ L3 Unified 32768 KiB (x2)
+ Load Average: 0.58, 0.53, 0.60
+ ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
+ ***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
+ ----------------------------------------------------------------------
+ Benchmark Time CPU Iterations
+ ----------------------------------------------------------------------
+ algorithm_lowercase_my 17.8 ns 17.7 ns 39664595
+ loop_lowercase_my 31.9 ns 31.9 ns 22028280
+ algorithm_lowercase_toupper 79.3 ns 79.2 ns 8838048
+ loop_lowercase_toupper 69.7 ns 69.5 ns 9977740
+---
+```shell
+ $ make release # g++ 15.2.1
+ $ ./build/release/transform
+```
+
+ 2026-02-07T15:43:03+02:00
+ Running ./build/release/transform
+ Run on (32 X 5187.5 MHz CPU s)
+ CPU Caches:
+ L1 Data 48 KiB (x16)
+ L1 Instruction 32 KiB (x16)
+ L2 Unified 1024 KiB (x16)
+ L3 Unified 32768 KiB (x2)
+ Load Average: 0.61, 0.54, 0.60
+ ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
+ ***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
+ ----------------------------------------------------------------------
+ Benchmark Time CPU Iterations
+ ----------------------------------------------------------------------
+ algorithm_lowercase_my 17.7 ns 17.7 ns 33607332
+ loop_lowercase_my 28.2 ns 28.2 ns 24781013
+ algorithm_lowercase_toupper 77.1 ns 77.0 ns 9116098
+ loop_lowercase_toupper 65.5 ns 65.4 ns 10687636
+---
+
+
+
+
+
+---
diff --git a/bench_lowercase.png b/bench_lowercase.png
deleted file mode 100644
index 1ef7ba2..0000000
Binary files a/bench_lowercase.png and /dev/null differ
diff --git a/bench_lowercase_clang17_cpp23.png b/bench_lowercase_clang17_cpp23.png
new file mode 100644
index 0000000..6f85297
Binary files /dev/null and b/bench_lowercase_clang17_cpp23.png differ
diff --git a/bench_lowercase_gcc13_cpp23.png b/bench_lowercase_gcc13_cpp23.png
new file mode 100644
index 0000000..323a282
Binary files /dev/null and b/bench_lowercase_gcc13_cpp23.png differ
diff --git a/src/transform.cpp b/src/transform.cpp
index bbe8159..6699b6c 100755
--- a/src/transform.cpp
+++ b/src/transform.cpp
@@ -3,13 +3,16 @@
#include
#include
#include
+#include
const char* data = "Hello world, to be or not to be a long string, that is the question!";
static void algorithm_lowercase_my(benchmark::State& state) {
+ std::string name;
+ name.reserve(strlen(data));
for (auto _ : state) { // Code inside this loop is measured repeatedly
- std::string name = data;
+ name = data;
std::transform(name.cbegin(), name.cend(), name.begin(), [](char c) noexcept {
return (c >= 'a' && c <= 'z') ? c = c - 'a' + 'A' : c;
});
@@ -19,8 +22,10 @@ static void algorithm_lowercase_my(benchmark::State& state) {
BENCHMARK(algorithm_lowercase_my); // Register the function as a benchmark
static void loop_lowercase_my(benchmark::State& state) {
+ std::string name;
+ name.reserve(strlen(data));
for (auto _ : state) { // Code before the loop is not measured
- std::string name = data;
+ name = data;
for (char& c : name) {
if (c >= 'a' && c <= 'z') {
c = c - 'a' + 'A';
@@ -33,8 +38,10 @@ BENCHMARK(loop_lowercase_my); // Register the function as a benchmark
static void algorithm_lowercase_toupper(benchmark::State& state) {
+ std::string name;
+ name.reserve(strlen(data));
for (auto _ : state) { // Code inside this loop is measured repeatedly
- std::string name = data;
+ name = data;
std::transform(name.cbegin(), name.cend(), name.begin(), [](char c) noexcept {
return std::toupper(static_cast(c));
});
@@ -44,6 +51,21 @@ static void algorithm_lowercase_toupper(benchmark::State& state) {
BENCHMARK(algorithm_lowercase_toupper); // Register the function as a benchmark
+static void loop_lowercase_toupper(benchmark::State& state) {
+ std::string name;
+ name.reserve(strlen(data));
+ for (auto _ : state) { // Code inside this loop is measured repeatedly
+ name = data;
+ for (char& c : name) {
+ if (c >= 'a' && c <= 'z') {
+ c = static_cast(std::toupper(c));
+ }
+ }
+ benchmark::DoNotOptimize(name); // Make sure the variable is not optimized away by compiler
+ }
+}
+BENCHMARK(loop_lowercase_toupper); // Register the function as a benchmark
+
int main(int argc, char** argv) {
benchmark::Initialize(&argc, argv);
if (benchmark::ReportUnrecognizedArguments(argc, argv)) {
@@ -53,16 +75,3 @@ int main(int argc, char** argv) {
benchmark::RunSpecifiedBenchmarks();
return EX_OK;
}
-
-static void loop_lowercase_toupper(benchmark::State& state) {
- for (auto _ : state) { // Code inside this loop is measured repeatedly
- std::string name = data;
- for (char& c : name) {
- if (c >= 'a' && c <= 'z') {
- c = static_cast(std::toupper(c));
- }
- }
- benchmark::DoNotOptimize(name); // Make sure the variable is not optimized away by compiler
- }
-}
-BENCHMARK(loop_lowercase_toupper); // Register the function as a benchmark