#include #include #include #include #include #include #include "benchmark_base.h" #include #include namespace yams::benchmark { /** * Benchmark literal extraction from regex patterns */ class LiteralExtractionBenchmark : public BenchmarkBase { public: LiteralExtractionBenchmark(const std::string& name, std::vector patterns, const Config& config = Config()) : BenchmarkBase("Grep_LiteralExtraction_" + name, config), patterns_(std::move(patterns)) {} protected: size_t runIteration() override { size_t extracted = 0; for (const auto& pattern : patterns_) { auto result = yams::app::services::LiteralExtractor::extract(pattern, false); if (result.longestLength > 0) { ++extracted; } } return extracted; } std::vector patterns_; }; /** * Benchmark Boyer-Moore-Horspool vs std::string::find */ class BMHSearchBenchmark : public BenchmarkBase { public: BMHSearchBenchmark(const std::string& name, std::string needle, size_t textSize, const Config& config = Config()) : BenchmarkBase("Grep_BMH_" + name, config), needle_(std::move(needle)) { // Generate haystack with a few needle occurrences text_.reserve(textSize); std::mt19937 rng(42); std::uniform_int_distribution charDist('a', 'z'); for (size_t i = 0; i < textSize; ++i) { // Insert needle every ~1000 chars if (i > 0 && i % 1000 == 0 && i + needle_.size() < textSize) { text_ += needle_; i += needle_.size(); } text_ += static_cast(charDist(rng)); } bmhSearcher_ = std::make_unique(needle_, false); } protected: size_t runIteration() override { size_t count = 0; size_t pos = 0; while ((pos = bmhSearcher_->find(text_, pos)) != std::string::npos) { ++count; ++pos; } return count; } std::string needle_; std::string text_; std::unique_ptr bmhSearcher_; }; /** * Benchmark std::string::find baseline */ class StdFindBenchmark : public BenchmarkBase { public: StdFindBenchmark(const std::string& name, std::string needle, size_t textSize, const Config& config = Config()) : BenchmarkBase("Grep_StdFind_" + name, config), needle_(std::move(needle)) { text_.reserve(textSize); std::mt19937 rng(42); std::uniform_int_distribution charDist('a', 'z'); for (size_t i = 0; i < textSize; ++i) { if (i > 0 && i % 1000 == 0 && i + needle_.size() < textSize) { text_ += needle_; i += needle_.size(); } text_ += static_cast(charDist(rng)); } } protected: size_t runIteration() override { size_t count = 0; size_t pos = 0; while ((pos = text_.find(needle_, pos)) != std::string::npos) { ++count; ++pos; } return count; } std::string needle_; std::string text_; }; /** * Benchmark SIMD newline scanning */ class SimdNewlineBenchmark : public BenchmarkBase { public: SimdNewlineBenchmark(const std::string& name, size_t textSize, size_t linesCount, const Config& config = Config()) : BenchmarkBase("Grep_SimdNewline_" + name, config) { // Generate text with known number of newlines std::mt19937 rng(42); std::uniform_int_distribution charDist('a', 'z'); size_t charsPerLine = textSize / linesCount; text_.reserve(textSize); for (size_t i = 0; i < linesCount; ++i) { for (size_t j = 0; j < charsPerLine; ++j) { text_ += static_cast(charDist(rng)); } text_ += '\n'; } } protected: size_t runIteration() override { size_t lineCount = 0; size_t pos = 0; while (pos < text_.size()) { size_t remaining = text_.size() - pos; size_t nlOffset = yams::app::services::SimdNewlineScanner::findNewline(text_.data() + pos, remaining); if (nlOffset >= remaining) { break; } ++lineCount; pos += nlOffset + 1; } return lineCount; } std::string text_; }; /** * Benchmark memchr baseline for newline scanning */ class MemchrNewlineBenchmark : public BenchmarkBase { public: MemchrNewlineBenchmark(const std::string& name, size_t textSize, size_t linesCount, const Config& config = Config()) : BenchmarkBase("Grep_MemchrNewline_" + name, config) { std::mt19937 rng(42); std::uniform_int_distribution charDist('a', 'z'); size_t charsPerLine = textSize / linesCount; text_.reserve(textSize); for (size_t i = 0; i < linesCount; ++i) { for (size_t j = 0; j < charsPerLine; ++j) { text_ += static_cast(charDist(rng)); } text_ += '\n'; } } protected: size_t runIteration() override { size_t lineCount = 0; const char* p = text_.data(); const char* end = p + text_.size(); while (p < end) { const char* nl = static_cast(memchr(p, '\n', static_cast(end - p))); if (!nl) { break; } ++lineCount; p = nl + 1; } return lineCount; } std::string text_; }; } // namespace yams::benchmark int main() { using namespace yams::benchmark; std::cout << "=== YAMS Grep Service Algorithmic Benchmarks ===" << std::endl; std::cout << "Measuring: Literal Extraction, BMH vs std::find, SIMD vs memchr" << std::endl; std::cout << std::endl; // Literal Extraction Benchmark { std::vector patterns = { R"(class\s+(\w+))", // class definition R"(function\s+\w+\s*\()", // function declaration R"(import\s+.*from)", // import statement R"(\b\d{3}-\d{2}-\d{4}\b)", // SSN pattern R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", // email }; BenchmarkBase::Config config; config.benchmark_iterations = 10000; config.warmup_iterations = 100; config.verbose = true; LiteralExtractionBenchmark bench("Common_Patterns", patterns, config); bench.run(); } // BMH vs std::find - Short Pattern (3 chars) { BenchmarkBase::Config config; config.benchmark_iterations = 1000; config.warmup_iterations = 10; std::cout << std::endl; std::cout << "--- Short Pattern (3 chars, 1MB text) ---" << std::endl; BMHSearchBenchmark bmhBench("ShortPattern", "foo", 1024 * 1024, config); bmhBench.run(); StdFindBenchmark stdBench("ShortPattern", "foo", 1024 * 1024, config); stdBench.run(); } // BMH vs std::find - Medium Pattern (8 chars) { BenchmarkBase::Config config; config.benchmark_iterations = 1000; config.warmup_iterations = 10; std::cout << std::endl; std::cout << "--- Medium Pattern (8 chars, 1MB text) ---" << std::endl; BMHSearchBenchmark bmhBench("MediumPattern", "function", 1024 * 1024, config); bmhBench.run(); StdFindBenchmark stdBench("MediumPattern", "function", 1024 * 1024, config); stdBench.run(); } // BMH vs std::find - Long Pattern (16 chars) { BenchmarkBase::Config config; config.benchmark_iterations = 1000; config.warmup_iterations = 10; std::cout << std::endl; std::cout << "--- Long Pattern (16 chars, 1MB text) ---" << std::endl; BMHSearchBenchmark bmhBench("LongPattern", "class_definition", 1024 * 1024, config); bmhBench.run(); StdFindBenchmark stdBench("LongPattern", "class_definition", 1024 * 1024, config); stdBench.run(); } // SIMD vs memchr - Small File (100KB, 1000 lines) { BenchmarkBase::Config config; config.benchmark_iterations = 5000; config.warmup_iterations = 50; std::cout << std::endl; std::cout << "--- Newline Scanning: Small File (100KB, 1000 lines) ---" << std::endl; SimdNewlineBenchmark simdBench("SmallFile", 100 * 1024, 1000, config); simdBench.run(); MemchrNewlineBenchmark memchrBench("SmallFile", 100 * 1024, 1000, config); memchrBench.run(); } // SIMD vs memchr - Large File (10MB, 100K lines) { BenchmarkBase::Config config; config.benchmark_iterations = 100; config.warmup_iterations = 5; config.verbose = true; std::cout << std::endl; std::cout << "--- Newline Scanning: Large File (10MB, 100K lines) ---" << std::endl; SimdNewlineBenchmark simdBench("LargeFile", 10 * 1024 * 1024, 100000, config); simdBench.run(); MemchrNewlineBenchmark memchrBench("LargeFile", 10 * 1024 * 1024, 100000, config); memchrBench.run(); } std::cout << std::endl; std::cout << "=== Benchmark Complete ===" << std::endl; return 0; }