#include "arena_allocator.hpp" #include "format.hpp" #include #include #include #include #include #if __cpp_lib_format >= 201907L #include #define HAS_STD_FORMAT 1 #else #define HAS_STD_FORMAT 0 #endif // Test data for consistent benchmarks constexpr int TEST_INT = 42; constexpr double TEST_DOUBLE = 3.14159; const std::string TEST_STRING = "Hello World"; // Benchmark simple string concatenation: "Hello " + "World" + "!" void benchmark_simple_concatenation() { std::cout << "\n=== Simple String Concatenation: 'Hello World!' ===\n"; ankerl::nanobench::Bench bench; bench.title("Simple Concatenation").unit("op").warmup(100).epochs(1000); // Arena-based static_format bench.run("static_format", [&] { ArenaAllocator arena(64); auto result = static_format(arena, "Hello ", "World", "!"); ankerl::nanobench::doNotOptimizeAway(result); }); // Arena-based format bench.run("format", [&] { ArenaAllocator arena(64); auto result = format(arena, "Hello %s!", "World"); ankerl::nanobench::doNotOptimizeAway(result); }); // std::stringstream bench.run("std::stringstream", [&] { std::stringstream ss; ss << "Hello " << "World" << "!"; auto result = ss.str(); ankerl::nanobench::doNotOptimizeAway(result); }); #if HAS_STD_FORMAT // std::format (C++20) bench.run("std::format", [&] { auto result = std::format("Hello {}!", "World"); ankerl::nanobench::doNotOptimizeAway(result); }); #endif // Raw snprintf with malloc bench.run("snprintf + malloc", [&] { char buffer[64]; int len = std::snprintf(buffer, sizeof(buffer), "Hello %s!", "World"); char *result = static_cast(std::malloc(len + 1)); std::memcpy(result, buffer, len + 1); ankerl::nanobench::doNotOptimizeAway(result); std::free(result); }); } // Benchmark mixed type formatting: "Count: 42, Rate: 3.14159" void benchmark_mixed_types() { std::cout << "\n=== Mixed Type Formatting: 'Count: 42, Rate: 3.14159' ===\n"; ankerl::nanobench::Bench bench; bench.title("Mixed Types").unit("op").warmup(100).epochs(1000); // Arena-based static_format bench.run("static_format", [&] { ArenaAllocator arena(128); auto result = static_format(arena, "Count: ", TEST_INT, ", Rate: ", TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); // Arena-based format bench.run("format", [&] { ArenaAllocator arena(128); auto result = format(arena, "Count: %d, Rate: %.5f", TEST_INT, TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); // std::stringstream bench.run("std::stringstream", [&] { std::stringstream ss; ss << "Count: " << TEST_INT << ", Rate: " << std::fixed << std::setprecision(5) << TEST_DOUBLE; auto result = ss.str(); ankerl::nanobench::doNotOptimizeAway(result); }); #if HAS_STD_FORMAT // std::format (C++20) bench.run("std::format", [&] { auto result = std::format("Count: {}, Rate: {:.5f}", TEST_INT, TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); #endif // Raw snprintf with malloc bench.run("snprintf + malloc", [&] { char buffer[128]; int len = std::snprintf(buffer, sizeof(buffer), "Count: %d, Rate: %.5f", TEST_INT, TEST_DOUBLE); char *result = static_cast(std::malloc(len + 1)); std::memcpy(result, buffer, len + 1); ankerl::nanobench::doNotOptimizeAway(result); std::free(result); }); } // Benchmark complex formatting with precision and alignment void benchmark_complex_formatting() { std::cout << "\n=== Complex Formatting: '%-10s %5d %8.2f' ===\n"; ankerl::nanobench::Bench bench; bench.title("Complex Formatting").unit("op").warmup(100).epochs(1000); // Arena-based format (static_format doesn't support printf specifiers) bench.run("format", [&] { ArenaAllocator arena(128); auto result = format(arena, "%-10s %5d %8.2f", TEST_STRING.c_str(), TEST_INT, TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); // std::stringstream bench.run("std::stringstream", [&] { std::stringstream ss; ss << std::left << std::setw(10) << TEST_STRING << " " << std::right << std::setw(5) << TEST_INT << " " << std::setw(8) << std::fixed << std::setprecision(2) << TEST_DOUBLE; auto result = ss.str(); ankerl::nanobench::doNotOptimizeAway(result); }); #if HAS_STD_FORMAT // std::format (C++20) bench.run("std::format", [&] { auto result = std::format("{:<10} {:>5} {:>8.2f}", TEST_STRING, TEST_INT, TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); #endif // Raw snprintf with malloc bench.run("snprintf + malloc", [&] { char buffer[128]; int len = std::snprintf(buffer, sizeof(buffer), "%-10s %5d %8.2f", TEST_STRING.c_str(), TEST_INT, TEST_DOUBLE); char *result = static_cast(std::malloc(len + 1)); std::memcpy(result, buffer, len + 1); ankerl::nanobench::doNotOptimizeAway(result); std::free(result); }); } // Benchmark error message formatting (common use case) void benchmark_error_messages() { std::cout << "\n=== Error Message Formatting: 'Error 404: File not found " "(line 123)' ===\n"; ankerl::nanobench::Bench bench; bench.title("Error Messages").unit("op").warmup(100).epochs(1000); constexpr int error_code = 404; constexpr int line_number = 123; const std::string error_msg = "File not found"; // Arena-based static_format (using string literals only) bench.run("static_format", [&] { ArenaAllocator arena(128); auto result = static_format(arena, "Error ", error_code, ": ", "File not found", " (line ", line_number, ")"); ankerl::nanobench::doNotOptimizeAway(result); }); // Arena-based format bench.run("format", [&] { ArenaAllocator arena(128); auto result = format(arena, "Error %d: %s (line %d)", error_code, error_msg.c_str(), line_number); ankerl::nanobench::doNotOptimizeAway(result); }); // std::stringstream bench.run("std::stringstream", [&] { std::stringstream ss; ss << "Error " << error_code << ": " << error_msg << " (line " << line_number << ")"; auto result = ss.str(); ankerl::nanobench::doNotOptimizeAway(result); }); #if HAS_STD_FORMAT // std::format (C++20) bench.run("std::format", [&] { auto result = std::format("Error {}: {} (line {})", error_code, error_msg, line_number); ankerl::nanobench::doNotOptimizeAway(result); }); #endif } // Benchmark memory allocation overhead by testing arena reuse void benchmark_memory_reuse() { std::cout << "\n=== Memory Allocation Patterns ===\n"; ankerl::nanobench::Bench bench; bench.title("Memory Patterns").unit("op").warmup(100).epochs(100); // Arena with fresh allocation each time (realistic usage) bench.run("fresh arena", [&] { ArenaAllocator arena(128); auto result = format(arena, "Test %d: %s %.2f", TEST_INT, TEST_STRING.c_str(), TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); // Pre-allocated arena (reuse scenario) ArenaAllocator shared_arena(1024); bench.run("reused arena", [&] { auto result = format(shared_arena, "Test %d: %s %.2f", TEST_INT, TEST_STRING.c_str(), TEST_DOUBLE); ankerl::nanobench::doNotOptimizeAway(result); }); // Fresh std::string allocations bench.run("std::stringstream", [&] { std::stringstream ss; ss << "Test " << TEST_INT << ": " << TEST_STRING << " " << std::fixed << std::setprecision(2) << TEST_DOUBLE; auto result = ss.str(); ankerl::nanobench::doNotOptimizeAway(result); }); } int main() { std::cout << "Format Function Benchmark Comparison\n"; std::cout << "====================================\n"; #if HAS_STD_FORMAT std::cout << "C++20 std::format: Available\n"; #else std::cout << "C++20 std::format: Not available\n"; #endif benchmark_simple_concatenation(); benchmark_mixed_types(); benchmark_complex_formatting(); benchmark_error_messages(); benchmark_memory_reuse(); std::cout << "\n=== Summary ===\n"; std::cout << "* static_format: Best for simple concatenation with known types\n"; std::cout << "* format: Best for printf-style formatting with arena allocation\n"; std::cout << "* std::stringstream: Flexible but slower due to heap allocation\n"; std::cout << "* std::format: Modern C++20 alternative (if available)\n"; std::cout << "* snprintf + malloc: Low-level but requires manual memory " "management\n"; return 0; }