From 9ce95725222edf2bcbc9c3e18e0881d901bd0d70 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Thu, 12 Oct 2023 14:16:04 -0700 Subject: [PATCH] Add a benchmark to stress test grouping symbols. (#3286) This isn't really representative of anything, but it should help make it obvious when the handling of grouping symbols improves or regresses. Notably, the random source microbenchmark has *no* grouping symbols (in order to let it be random but always lexically valid), and so it's especially useful to have something that checks grouping symbols given their prevalence in realistic source code. --- toolchain/lex/tokenized_buffer_benchmark.cpp | 86 ++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/toolchain/lex/tokenized_buffer_benchmark.cpp b/toolchain/lex/tokenized_buffer_benchmark.cpp index fd05369455f3..3a0f34c6c5d7 100644 --- a/toolchain/lex/tokenized_buffer_benchmark.cpp +++ b/toolchain/lex/tokenized_buffer_benchmark.cpp @@ -498,6 +498,92 @@ void BM_RandomSource(benchmark::State& state) { // range here. BENCHMARK(BM_RandomSource); +// Benchmark to stress opening and closing grouped symbols. +void BM_GroupingSymbols(benchmark::State& state) { + int curly_brace_depth = state.range(0); + int paren_depth = state.range(1); + int square_bracket_depth = state.range(2); + + // TODO: It might be interesting to have some random pattern of nesting, but + // the obvious ways to do that result it really unstable total size of input + // or unbalanced groups. For now, just use a simple strict nesting approach. + // It should still let us look for specific pain points. We do include some + // whitespace and keywords to make sure *some* other parts of the benchmark + // are also active and have some reasonable icache pressure. + const std::array& ids = GetRandomIdentifiers(); + std::string source; + llvm::raw_string_ostream os(source); + int num_tokens_per_nest = + curly_brace_depth * 2 + paren_depth * 2 + square_bracket_depth * 2 + 2; + int num_nests = NumTokens / num_tokens_per_nest; + for (int i : llvm::seq(num_nests)) { + for (int j : llvm::seq(curly_brace_depth)) { + os.indent(j * 2) << "{\n"; + } + os.indent(curly_brace_depth * 2); + for ([[gnu::unused]] int j : llvm::seq(paren_depth)) { + os << "("; + } + for ([[gnu::unused]] int j : llvm::seq(square_bracket_depth)) { + os << "["; + } + os << ids[(i * 2) % NumTokens]; + for ([[gnu::unused]] int j : llvm::seq(square_bracket_depth)) { + os << "]"; + } + for ([[gnu::unused]] int j : llvm::seq(paren_depth)) { + os << ")"; + } + for (int j : llvm::reverse(llvm::seq(curly_brace_depth))) { + os << "\n"; + os.indent(j * 2) << "}"; + } + os << ids[(i * 2 + 1) % NumTokens] << "\n"; + } + + LexerBenchHelper helper(os.str()); + for (auto _ : state) { + TokenizedBuffer buffer = helper.Lex(); + + // Ensure that lexing actually occurs for benchmarking and that it doesn't + // hit errors that would skew the benchmark results. + CARBON_CHECK(!buffer.has_errors()) << helper.DiagnoseErrors(); + } + + state.SetBytesProcessed(state.iterations() * source.size()); + state.counters["tokens_per_second"] = benchmark::Counter( + NumTokens, benchmark::Counter::kIsIterationInvariantRate); + state.counters["lines_per_second"] = + benchmark::Counter(llvm::StringRef(source).count('\n'), + benchmark::Counter::kIsIterationInvariantRate); +} +BENCHMARK(BM_GroupingSymbols) + ->ArgsProduct({ + {1, 2, 3, 4, 8, 16, 32}, + {0}, + {0}, + }) + ->ArgsProduct({ + {0}, + {1, 2, 3, 4, 8, 16, 32}, + {0}, + }) + ->ArgsProduct({ + {0}, + {0}, + {1, 2, 3, 4, 8, 16, 32}, + }) + ->ArgsProduct({ + {32}, + {1, 2, 3, 4, 8, 16, 32}, + {0}, + }) + ->ArgsProduct({ + {32}, + {32}, + {1, 2, 3, 4, 8, 16, 32}, + }); + // Benchmark to stress the lexing of blank lines. This uses a simple, easy to // lex token, but separates each one by varying numbers of blank lines. void BM_BlankLines(benchmark::State& state) {